Обобщения Java: незаконная прямая ссылка - PullRequest
8 голосов
/ 07 января 2011

С учетом универсального интерфейса

interface Foo<A, B> { }

Я хочу написать реализацию, которая требует, чтобы A был подклассом B. Поэтому я хочу сделать

class Bar<A, B super A> implements Foo<A, B> { }
// --> Syntax error

или

class Bar<A extends B, B> implements Foo<A, B> { }
// --> illegal forward reference

Но единственное решение, которое, кажется, работает, это:

class Bar<B, A extends B> implements Foo<A, B> { }

, что довольно уродливо, потому что оно меняет порядок общих параметров.
Есть ли какие-либо решения или обходные пути для этой проблемы?

Ответы [ 3 ]

5 голосов
/ 07 января 2011

Поскольку в Java это невозможно, попробуйте думать о Bar<B, A extends B> по-другому.

Когда вы объявляете переменную для Bar, вы указываете родительский класссначала, а затем дочерний класс.Вот как Bar работает.Не думайте об этом как об обратном - думайте об этом как о нападающих.Родитель, естественно, должен быть указан до ребенка.Это дополнительное отношение, которое вы добавили, определяет порядок параметров, а не базовый интерфейс.

1 голос
/ 08 января 2011

Уже указывалось, что нет ни решения, ни хорошего обходного пути.Вот что я наконец-то сделал.Это работает только для моего особого случая, но вы можете взять это как вдохновение, если столкнетесь с подобными проблемами.(Это также объясняет, почему я столкнулся с этой проблемой)

Прежде всего, существует этот класс (показывает только соответствующий интерфейс):

class Pipe<Input, Output> {

    boolean hasNext();

    Input getNext();

    void setNext(Output o);

}

Интерфейс Foo на самом деле

interface Processor<Input, Output> {

    process(Pipe<Input, Output> p);

}

и класс Bar должен работать следующим образом

class JustCopyIt<Input, Output> implements Processor<Input, Output> {

    process(Pipe<Input, Output> p) {
       while (p.hasNext()) p.setNext(p.getNext());
    }

}

Самый простой способ - привести значения следующим образом: p.setNext((Output) p.getNext()).Но это плохо, поскольку это позволило бы создать экземпляр JustCopyIt<Integer, String>.Вызов этого объекта может произойти по какой-то таинственной причине, но не в тот момент, когда будет допущена фактическая ошибка.

Выполнение class JustCopyIt<Type> implements Processor<Type, Type> также не сработает, потому что тогда я не могу обработать Pipe<String, Object>,

Итак, в конце концов я изменил интерфейс следующим образом:

interface Processor<Input, Output> {

    process(Pipe<? extends Input, ? super Output> p);

}

Таким образом, JustCopyIt<List> может обрабатывать Pipe<ArrayList, Collection>.

Хотя это технически кажется единственным допустимым решением, оно все же плохо, потому что оно 1) работает только для этого особого случая, 2) потребовало от меня изменить интерфейс (что не всегда возможно) и 3) сделал код другогопроцессоры безобразны.

Редактировать:
Чтение ответа Китса снова вдохновило меня на другое решение:

public abstract class Bar<A, B> implements Foo<A, B> {

    public static <B, A extends B> Bar<A, B> newInstance() {
        return new BarImpl<B, A>();
    }

    private static class BarImpl<B, A extends B> extends Bar<A, B> {
        // code goes here
    }

}

// clean code without visible reversed parameters
Bar<Integer, Object> bar1 = Bar.newInstance();
Bar<Object, Integer> bar2 = Bar.newInstance(); // <- compile error
1 голос
/ 07 января 2011

Посмотрев на этот вопрос, я потратил немного времени на то, чтобы попробовать разные техники, которые, по моему мнению, могли бы работать. Например, создание универсального интерфейса ISuper<B,A extends B> и затем Bar<A,B> implements ISuper<B,A> (и подобный метод с подклассом и расширяет, а не реализует), но это просто приводит к ошибке типа Bar.java:1: type parameter A is not within its bound. Аналогичным образом я попытался создать метод private <A extends B> Bar<A,B> foo() { return this; }; и вызвать его из конструктора, но это просто приводит к сообщению об ошибке забавного типа Bar.java:2: incompatible types found : Bar<A,B> required: Bar<A,B>

Итак, я думаю, что, к сожалению, ответ - нет. Очевидно, это не тот ответ, на который вы надеялись, но, похоже, правильный ответ заключается в том, что это просто невозможно.

...