Связать два универсальных типа - PullRequest
3 голосов
/ 07 марта 2012

У меня вопрос по дженерикам.У меня есть следующие интерфейсы:

public interface InterfaceA<B extends InterfaceA.InterfaceB> {

    public interface InterfaceB<A extends InterfaceA> {

        void setA(A a);
    }
}

И следующая абстрактная реализация InterfaceA:

public abstract class AImplOne<B extends InterfaceA.InterfaceB> implements InterfaceA<B> {

    private final B b;

    public AImplOne(B b) {
        this.b = b;
        b.setA(this); // <-- Unchecked call...
    }
}

Мне ясно, что вызов b.setA(this) не проверен - но ямне это не понравилось, поэтому я попробовал вторую абстрактную реализацию:

public abstract class AImplTwo<A extends InterfaceA, B extends InterfaceA.InterfaceB<A>> implements InterfaceA<B> {

    private final B b;

    public AImplTwo(B b) {
        this.b = b;
        b.setA((A)this); // <-- Unchecked cast
    }
}

И снова мне ясно, что вызов b.setA((A)this) - это отмена проверки.

Но как это реализовать или изменить, чтобы избавиться от непроверенного кода?

1 Ответ

2 голосов
/ 07 марта 2012

У вас фактически есть взаимное рекурсивное универсальное определение, которое вы разбиваете с использованием необработанных типов: в

b.setA((A)this); // <- Unchecked cast

this имеет тип InterfaceA<? extends InterfaceA.InterfaceB<? extends InterfaceA>>, но должен иметь тип InterfaceA<? extends InterfaceA.InterfaceB<? extends InterfaceA<? extends InterfaceA.InterfaceB<...>>>>. Вы должны использовать вместо

public interface InterfaceA<B extends InterfaceA.InterfaceB<?>> {

    public interface InterfaceB<A extends InterfaceA<B>> { //<- cannot make a static reference to the non-static type B

        void setA(A a);
    }
}

но вы не можете использовать B, который не является статичным, в объявлении статического интерфейса (объявления интерфейса всегда статические).


Для подробностей, еще одна попытка: Использование альтернативы

public interface InterfaceA<B extends InterfaceA.InterfaceB<?>> {

    public interface InterfaceB<A extends InterfaceA<? extends InterfaceA.InterfaceB<?>>> {

        void setA(A a);
    }
}


abstract class AImplTwo<B extends InterfaceA.InterfaceB<A>, A extends InterfaceA<B>> implements InterfaceA<B> {

    private final B b;

    public AImplTwo(B b) {
        this.b = b;
        b.setA((A)this); // <-- Unchecked cast
    }
}

снова вызывает неконтролируемое приведение, поскольку теперь параметр вложенного типа InterfaceA в interface InterfaceB<A extends InterfaceA<? extends InterfaceA.InterfaceB<?>>> снова является произвольным подклассом InterfaceA.InterfaceB<?>.


Обновление, поскольку вы запросили общий дизайн:

Я бы подумал об InterfaceB (фактически, об интерфейсах в целом) как об абстракции от конкретной реализации: вам нужен только интерфейс InterfaceB, а не детали его реализации, в вашей реализации InterfaceA. Думайте о InterfaceB как о контракте, и вы не заботитесь о реализации. Следовательно, нет необходимости связывать реализацию InterfaceA с реализацией InterfaceB:

public interface InterfaceA {

    public interface InterfaceB {

        void setA(InterfaceA a);
    }
}

Только если по причинам, которые я не вижу, вы хотите иметь один и тот же тип для всех экземпляров InterfaceB, которые вы используете, вам нужны универсальные шаблоны. И наоборот для InterfaceA. В последнем вышеприведенном примере обобщенных типов вы, по крайней мере, можете исправить типы для InterfaceA и InterfaceB, и вам нужно будет только динамически утверждать, что B B и A одинаковы.

Показывать, что в Java нет решения с проверкой типа, сложно, но, возможно, оно станет правдоподобным в следующем примере, который был бы решением, если бы Java допускала комбинацию extends и super:

public interface A<TB extends A.B<?>> {

    public interface B<TA extends A<? extends A.B<?>>> {

        void setA(TA a);
    }
}


class AImplTwo<TB extends A.B<TA>, TA extends AImplTwo<TB, TA> super AImplTwo<TB, TA>> implements A<TB> {

    private final TB b;

    public AImplTwo(TB b) {
        this.b = b;
        b.setA((TA)this);
    }
}

... если подумать, система Pluggable Type System, которая добавляет дополнительную типизацию в Java, позволяет использовать эту комбинацию extends и super и, следовательно, может предложить решение вашей проблемы. Но я нахожу это слишком сложным для того, что вы получаете, и будет придерживаться простого использования интерфейсов без шаблонов или какого-либо непроверенного приведения.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...