Java-дженерики: недопустимая обратная ссылка

Учитывая общий интерфейс

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> { }

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

Ответ 1

Так как это невозможно в Java, попробуйте иначе Bar<B, A extends B>.

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

Ответ 2

Увидев этот вопрос, я немного потрудился попробовать разные методы, которые, как я думал, могут работать. Например, построим общий интерфейс 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; }; и вызывать его из конструктора, но это просто приводит к сообщению об ошибке типа fun Bar.java:2: incompatible types found : Bar<A,B> required: Bar<A,B>

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

Ответ 3

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

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

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> также не будет работать здесь, потому что тогда я не могу обработать a Pipe<String, Object>.

Итак, что я, наконец, сделал, это изменить интерфейс к этому:

interface Processor<Input, Output> {

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

}

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

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

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

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