【问题标题】:Bind two generic types绑定两个泛型
【发布时间】:2012-03-24 19:22:32
【问题描述】:

我有一个关于泛型的问题。 我有以下接口:

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) 是一个未选中的演员。

但是应该如何实现或重新设计以摆脱未经检查的代码呢?

【问题讨论】:

  • 在一个关于泛型的问题中使用一个字母的类名A 非常非常令人困惑
  • 除了使用嵌套类,AImpl 应该是AImplOneAImplTwo,问题名称中有错字,你没有B 的类型参数(这里:B&lt;TA extends A*&lt;???&gt;*&gt;。这可能是个问题,但您的示例代码有点过于复杂。
  • @artbristol:好点:将 A 重命名为 InterfaceAB 重命名为 InterfaceB

标签: java generics interface abstract-class


【解决方案1】:

你实际上有一个相互递归的泛型定义,你通过使用原始类型来打破它:在

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

thisInterfaceA&lt;? extends InterfaceA.InterfaceB&lt;? extends InterfaceA&gt;&gt; 类型,但它应该是 InterfaceA&lt;? extends InterfaceA.InterfaceB&lt;? extends InterfaceA&lt;? extends InterfaceA.InterfaceB&lt;...&gt;&gt;&gt;&gt; 类型。您将不得不使用

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

再次导致未经检查的强制转换,因为现在interface InterfaceB&lt;A extends InterfaceA&lt;? extends InterfaceA.InterfaceB&lt;?&gt;&gt;&gt; 中的InterfaceA 的嵌套类型参数又是InterfaceA.InterfaceB&lt;?&gt; 的任意子类。


更新,因为您要求提供通用设计:

我认为 InterfaceB(实际上是一般的接口)是对具体实现的抽象:在 InterfaceA 的实现中,您只需要接口 InterfaceB,而不需要它的实现细节。将 InterfaceB 视为合约,您并不关心实现。因此不需要将 InterfaceA 的实现绑定到 InterfaceB 的实现:

public interface InterfaceA {

    public interface InterfaceB {

        void setA(InterfaceA a);
    }
}

仅当出于我看不到的原因,您确实希望您正在使用的所有 InterfaceB 实例都具有相同的类型时,您才需要泛型。 InterfaceA 反之亦然。使用上面最后一个泛型示例,您至少可以修复 InterfaceA 和 InterfaceB 的类型,并且只需要动态断言 A 的 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);
    }
}

...想一想,可插入类型系统为 Java 添加了更多类型,允许扩展和超级的这种组合,因此可能会为您的问题提供解决方案。但我发现它对于你得到的东西来说太复杂了,要么坚持简单地使用没有泛型的接口,要么坚持使用一些未经检查的强制转换。

【讨论】:

  • 好的,所以这只能使用原始类型来完成。但是如何更优雅地解决这个问题呢?一般问,如何让抽象子类中的实现相互认识?
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2019-07-02
  • 2019-12-07
  • 2017-07-01
  • 2016-04-10
  • 1970-01-01
相关资源
最近更新 更多