Java接口 - 泛型

3

我有两个接口:接口A和继承接口A的接口B。现在,我有一个类想要保留实现了接口A或B的对象的引用,并且我希望提供它的setter方法。我尝试了以下代码,但是出现了类型不匹配的异常。

public interface A {
}

public interface B extends A {
}

public class AB {
private Class<? extends A> object;

public void setObject(Class<? extends A> o){
   this.object = o;
}
}

基本上,我希望setObject方法能够接受实现接口A或B的对象。

1
设置对象 setObject(A a) - Jordão
@Jordão 将其设置为答案,并包含一个描述为什么它有效(多态性)的链接,以获得我的+1。 - pickypg
我刚刚添加了一个FYI答案 - 每个人都在解释有界通配符的工作原理,但完全忽略了你的代码是正确的,你只是误解了类和对象之间的区别。 - James Bassett
4个回答

4

简单的回答:

将其类型设置为AsetObject(A a)

实现B类的类也会实现A。完整代码:

public class UsesA {
  private A a;

  public void setObject(A a){
    this.a = a;
  }
}

现在,如果您真的想要使用B工作,您应该将其键入为B,这样也可以将其视为A,因为B从中继承:

public class UsesB {
  private B b; // can call A's methods on this field

  public void setObject(B b) {
    this.b = b;
  }
}

但现在你无法将一个 A (静态) 引用传递给 setObject。如果你真的想要这样做,那么你需要先将其下行转换为 B,这可能在运行时失败。泛型不会改善这种情况。


1
多态是面向对象编程中的一件美妙的事情。 - pickypg
@Jordao:感谢您的回答。但是如果将来接口B添加了新方法,那我不是必须要进行向上转型吗? - Arjun Patel
要使用 B,你必须进行向下转型,这是不安全的。但是,如果你想使用 B,你应该将其类型声明为 B,而不是 A。记住,B 也是 A,所以你仍然可以使用 B 实例来操作 A - Jordão
是的,我必须进行向下转换。我同意您的观点,这样做并不安全。所以,当您说“将类型视为B”时,意思是在setObject中传递B,对吗? - Arjun Patel
@Jordao:这就是让我困惑的地方。根据我的原始问题,我想要A或B。公共类UsesAorB{}。 - Arjun Patel
@ArjunPatel:但是如果你想调用只存在于 B 上的方法,那么你需要使用 B(因为它继承自 A,所以也会得到 A)。如果你将其类型定义为 A,则无法在其上调用 B 的方法。 - Jordão

2
如果您有一个实现了B的对象,它也将是A的一个实例。因此,您可以使setObject接受任何A,这将允许将AB实例传递给它。
public void setObject(A a){
    this.object = a;
}

2
您的代码实际上与您的问题不符。您已经说明了,

您有一个类,它希望保持对实现A或B接口对象的引用,但该字段(称为“object”)实际上是一个类,而不是一个类的实例。

如果您真正想让您的setter接受扩展A的任何接口,则您的代码将起作用。但是,正如您可能从其他答案中现在意识到的那样,您实际上想要一个A的实例 :)
public class AB {
    private Class<? extends A> type; // renamed from object for clarity

    public void setObject(Class<? extends A> type) {
        this.type = type;
    }

    @Test
    public void testSetter() {
        setObject(A.class); // no errors
        setObject(B.class); // no errors
    }
}

0

如先前建议的那样,如果您使用(A a),它将起作用,因为B是A的一种类型。因此,子类始终可以表示其父类。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接