У вас фактически есть взаимное рекурсивное универсальное определение, которое вы разбиваете с использованием необработанных типов: в
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 и, следовательно, может предложить решение вашей проблемы. Но я нахожу это слишком сложным для того, что вы получаете, и будет придерживаться простого использования интерфейсов без шаблонов или какого-либо непроверенного приведения.