覆盖Java中的抽象generics方法

问题大纲

我正在弄清楚我当前项目基础的更好部分,我有一个想法,我决定测试有关覆盖抽象方法。 这是我在Java中的测试类:

public abstract class Base { public abstract  T test(); } 

首次实施:

 public class Inheritor extends Base { @Override public Inheritor test() { return null; } } 

第二次实施:

 public class Inheritor2 extends Base { @Override public  T test() { return null; } } 

问题1

为什么要编译? 我承认我很有希望它是合法的,因为它使合同不仅确保它返回一些扩展Base的东西,而且已经更加专业化了(因此我不需要在以后的某个地方将结果转换为我的专业类) )。

一切听起来不错但是我真的履行了基类强迫我的合同吗? 我在Inheritor中的重写实现失去了某些通用层,不是吗? 我在Inheritor中实现此方法并不会返回一个Inheritor2的实例,抽象方法似乎强制执行的可能性(因为它们都扩展了Base )。

我想指出文档中的一些摘录。 我的猜测是它与类型擦除有关,如果有人在他/她的答案中提到它的准确性会很好。

问题2

这个程序是否有正式名称,而不是我在标题中说明的名称?

问题3

这可能在C#中吗? 同事的划痕测试似乎在编译时失败了。 那么通用抽象方法覆盖的方法是否存在差异?

以下是技术细节。

关于覆盖 :

在类C声明或inheritance的实例方法mC ,覆盖C类中声明的另一个方法mA ,iff以下所有条件都为真:

  • AC的超类。
  • C不inheritancemA
  • mC的签名是mA签名的子签名(第8.4.2节)。
  • 以下之一是真的:
    • mApublic
    • […]

在您的情况下, ABaseCInheritorBase#test()mAInheritor#test()mC

mCmA的子信号, 因为

方法m1的签名是方法m2的签名的子签名,如果: – m2具有与m1相同的签名,或者 – m1的签名与m2的签名的擦除(§4.6)相同。

mA的擦除是

 public abstract Base test() 

mC

 public Inheritor test() 

是一个副主题。 返回类型怎么样?

如果具有返回类型R1的方法声明d1覆盖或隐藏具有返回类型R2的另一个方法d2的声明,则对于d2d1必须是return-type-substitutable(第8.4.5节) ,否则会发生编译时错误。

return-type-substitutable ,我们看到了

如果R1是引用类型,则以下之一为真:

  • 可以通过未经检查的转换将R1转换为R2的子类型(第5.1.9节)。

InheritorT extends Base的子类型,通过未经检查的转换,所以我们都很好(尽管你应该得到编译器的警告)。

所以回答你的问题:

  1. 它由于Java语言规范中声明的规则而编译。
  2. 它被称为压倒一切。
  3. 我没有完整的答案,但C#似乎没有类型擦除,所以这些规则不适用。

未经检查的转换的危险将允许您这样做

 class Inheritor extends Base { @Override public Inheritor test() { return new Inheritor(); } } 

接着

 Base ref = new Inheritor(); Inheritor2 wrong = ref.test(); 

这会在运行时导致ClassCastException 。 需要您自担风险使用它。

我可以告诉你为什么它应该工作 – 利斯科夫替代原则

要问的问题是,如果将Base替换为inheritance者或inheritance者2,所有消费者是否会继续工作而不会产生负面影响? 如果他们期望从test扩展Base的任何东西,那么从使用者的角度来看,将Inheritor2与Inheritor交换,反之亦然。 所以,编译器应该允许它。

您确实履行了合同,该合同表明可以返回任何Base子类型。 任何子类型都可以是一个子类型,一个随机子类型等。

就像评论者Elliott一样,我相信它只是被称为覆盖一种方法。

这是C#中的类似实现,但在类级别上有generics。

  public abstract class Base where Type : Base { public abstract Type test(); } public class Inheritor:Base { public override Inheritor test() { return null; } } public class Inheritor2 : Base where Type : Base { public override Type test() { return default(Type); } }