【问题标题】:Java: using generic wildcards with subclassingJava:使用带有子类化的通用通配符
【发布时间】:2011-01-07 14:14:57
【问题描述】:

假设我有一个类 Foo、一个类 A 和 A 的一些子类 B。Foo 接受 A 及其子类作为泛型类型。 A 和 B 在其构造函数中都需要一个 Foo 实例。我希望 A 的 Foo 为 A 类型,B 的 Foo 为 B 类型或 B 的超类。所以实际上,我只想要这个:

Foo<X> bar = new Foo<X>;
new B(bar);

如果 X 是 A、B 或 A 的子类和 B 的超类,则可能。

到目前为止,这是我所拥有的:

class Foo<? extends A>{
    //construct
}


class A(Foo<A> bar){
    //construct
}

class B(Foo<? super B> bar){
    super(bar);
    //construct
}

super(...) 的调用不起作用,因为&lt;A&gt;&lt;? super B&gt; 更严格。在强制执行这些类型时是否可以使用构造函数(或通过其他方式避免代码重复)?

编辑:Foo 保留了泛型参数类型的元素的集合,这些元素和 Foo 具有双向链接。因此,应该不可能将 A 链接到 Foo。

【问题讨论】:

  • 这看起来不是您的实际代码。
  • 是的,它是代码的简化版本。你认为我错过了什么吗?

标签: java generics inheritance constructor


【解决方案1】:

如果将 A 构造函数更改为:

class A(Foo<? extends A> bar){
    //construct
}

它会做你想做的事吗?

如果您真的想将 A 的构造函数限制为 Foo ,那么您需要提供另一个受保护的方法(也可以从派生类中使用)来设置 Foo 实例。 像这样的:

public class A {

    Foo<?> foo;

    public A(Foo<A> foo) {
        setFoo(foo);
    }

    protected A() {
    }

    protected void setFoo(Foo<?> foo) {
        this.foo = foo;
    }
}

和B

public class B extends A {

    public B(Foo<? super B> foo) {
        setFoo(foo);
    }
}

现在可以了:

new A(new Foo<A>());
new A(new Foo<B>()); // this fails compilation
new B(new Foo<B>());

为了正确键入 A 中的 foo 元素,您可能还需要将 A 设为参数化类。

【讨论】:

  • 是的,似乎没有办法避免这种可能性
  • 呵呵 :) 我刚刚用我认为更好的变体更新了答案。
  • 这似乎是一个很好的解决方案,谢谢! :) 太糟糕了,它对子类/包并不完全透明,但我想这在这里是不可能的。
【解决方案2】:

做到这一点的唯一方法就是...

class A(Foo<? extends A> bar) {
  //construct
}

但这似乎不是你想要的。您不能采用其他方法,因为当您创建 B 的实例时,您也在创建 A 的实例(即 B 实例的一部分)。所以 B 不能为 A 中的部分输入特殊字段。我不确定你为什么不允许 A 的 foo 是 B 类型,你能不能扩展一下?

【讨论】:

  • 嗯,Foo 保存了一个泛型参数类型的元素的集合,这些元素和 Foo 有一个双向链接。也许我应该提到这一点。因此,如果我有一个包含 B 的 Foo,我想阻止将 A 链接到它。
【解决方案3】:

为我编译的以下设置:

public interface MarkerInterface {

}

public class A implements MarkerInterface {

    public A(Foo<A> fooA) {

    }
}

public class SuperB implements MarkerInterface {

}


public class B extends SuperB {

    public B(Foo<? super B> fooB) {

    }
}

使用main 方法:

public static void main(String[] args) {
    B b = new B(new Foo<SuperB>());
}

这是你要找的吗?

【讨论】:

  • 感谢您的建议,但此方法删除了类层次结构。我想这个想法是将A的方法签名放在MarkerInterface中?在 B 中复制所有 A 的代码对我来说似乎并不理想。
  • 好吧,我真的不知道如何帮助你,因为你刚刚扩展了你的场景。
【解决方案4】:

Java 泛型功能强大且设计精良;然而,我们有时会发现它们缺乏。我认为没有什么好方法可以做你想做的事。

最简单的做法是将Foo&lt;X&gt; 的声明更改为Foo&lt;X extends A&gt;。如果这不可接受,您可以像这样子类化Foo

class Foo<X> { }

class FooA<X extends A> extends Foo<X> { }

class A {
    public A(FooA<? extends A> foo) { }
}

class B extends A {
    public B(FooA<? super B> foo) {
        super(foo);
    }
}

(注意:如果你在关注时遇到问题,当你看到Foo时,想想ArrayList。)

这有一个明显的缺点,就是你必须使用FooA而不是Foo,阻碍了代码重用。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2010-09-18
    • 2014-08-27
    • 1970-01-01
    • 1970-01-01
    • 2022-09-23
    • 1970-01-01
    • 2014-09-04
    • 2014-04-14
    相关资源
    最近更新 更多