【问题标题】:How do Java generic parameters work in methods that are overriden?Java 泛型参数如何在被覆盖的方法中工作?
【发布时间】:2017-02-17 03:08:46
【问题描述】:

我有两个课,

A.java

public abstract class A {
    @Override
    public abstract <T extends A> T add();

    @Override
    public abstract <T extends A> T addWithParam(T param);
}

B.java

public class B extends A {
    @Override
    public B add() {...}

    @Override
    public B addWithParam(B param) {...}
}

add() 方法编译,但addWithParam 产生错误,说 addWithParam(B) 必须覆盖更高类之一中的方法(在这种情况下为 A)。一个修复方法是完全丢失覆盖注释,但我的意图是强制用户覆盖 B 类中的方法并返回 B 类型的对象。

我只能想出两种方法来解决这个问题。第一个解决方案是将返回类型从泛型类型T 设置为A。这将允许我返回类型 A,但是用户必须将返回结果从 B.addWithParam(B) 转换为类型 B,这是我试图避免的。

另一个解决方案是将抽象方法的可见性从公共更改为受保护(在A.java 中)。在前面提到的解决方案中,我将返回类型更改为AA 的继承类创建了一个返回(B) B.addWithParam(B param) 的方法(请记住B.addWithParam(B param) 返回类型为A,由超类指定)。

那么这里的问题是,为什么会产生原来的错误呢?我只是写错了代码,还是 Java 直接不支持我想要实现的目标?

【问题讨论】:

  • 编译器出错的直接原因是在抽象类中,在运行时,addWithParam()方法将Object作为输入,同时也返回了Object。在您的覆盖版本中(实际上并没有真正覆盖),您将具体的 B 类型作为输入,然后还返回 B 类型。
  • @TimBiegeleisen:反例:考虑A声明为class A&lt;T&gt;,方法为T addWithParam(T param)B声明为class B extends A&lt;B&gt;B' s addWithParamB addWithParam(B param)。您的前提也适用于这种情况——在运行时,AaddWithParam 采用 Object 并返回 Object,而在覆盖版本中,它采用 B 并返回 B - - 所以根据你的逻辑,它应该是无效的。然而,这种情况是完全有效的,并且可以正确覆盖。

标签: java generics


【解决方案1】:
public abstract <T extends A> T add();

这个方法声明的意思是,对于任何a 类型的A 或子类型的对象,您可以调用方法a.add() 并取回A 的任何子类型调用者 想要。因此,如果您有class B extends Aclass C extends A,那么您刚才告诉Java 的是以下应该可以工作:

C c = myB.add();

这不是你的意思。相信我。

public abstract A add();

这意味着在任何A 上调用add() 至少可以让您返回A,并且A 的子类型可以更详细地指定它们返回的确切类型,因此您可以编写

class B extends A {
   public B add() { ... }
}

这样比较合理。这个不允许让你做的是指定addWithParam 方法将只接受B,而不是另一个A 类型。

没有任何方法可以强制子类返回其自身类型的实例。人们使用各种技巧和提示来实现不同程度的有效性,例如:

class A<T extends A> {
  abstract T add();
  abstract T addWithParam(T t);
}
class B extends A<B> {
  B add() { ... }
  B addWithParam(B b) { ... }
}

这至少做了你想做的事。它不会阻止某人写class C extends A&lt;B&gt;

【讨论】:

  • 我同意您的回答,但我不会将其称为您所说的有效解决方案。当我们编写接口/类时,我们声明无论您放置泛型类型的什么,都将在该方法中返回。如果我选择将 xyz 作为泛型类型,我希望从这种方法中返回它。
  • 我认为这是一个hack,因为它不能保证T实际上是“自我”类型;就像我说的,你仍然可以写C extends A&lt;B&gt;,这违反了设计的意图。
  • 我同意不可能强制使用与问题中所问的泛型类型相同的类,但是了解 java 框架中的约束/限制,没有人会对class C extends A&lt;B&gt; 的情况。尽管它可能并非在所有情况下都有意义。
  • 感谢您的意见!我正在考虑将超级返回类型更改为 A,但这会导致我遇到有人写 C.addWithParam(A) 返回类型为 B 的问题。无论哪种方式,这两种解决方案最终都会依赖用户返回预期的类型。再次感谢!
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2013-08-22
  • 1970-01-01
  • 2020-06-03
相关资源
最近更新 更多