覆盖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以下所有条件都为真:
A
是C
的超类。C
不inheritancemA
。mC
的签名是mA
签名的子签名(第8.4.2节)。- 以下之一是真的:
mA
是public
。- […]
在您的情况下, A
是Base
, C
是Inheritor
, Base#test()
是mA
, Inheritor#test()
是mC
。
mC
是mA
的子信号, 因为
方法m1的签名是方法m2的签名的子签名,如果: – m2具有与m1相同的签名,或者 – m1的签名与m2的签名的擦除(§4.6)相同。
mA
的擦除是
public abstract Base test()
和mC
public Inheritor test()
是一个副主题。 返回类型怎么样?
如果具有返回类型
R1
的方法声明d1
覆盖或隐藏具有返回类型R2
的另一个方法d2
的声明,则对于d2
,d1
必须是return-type-substitutable(第8.4.5节) ,否则会发生编译时错误。
在return-type-substitutable
,我们看到了
如果R1是引用类型,则以下之一为真:
- 可以通过未经检查的转换将
R1
转换为R2
的子类型(第5.1.9节)。
Inheritor
是T extends Base
的子类型,通过未经检查的转换,所以我们都很好(尽管你应该得到编译器的警告)。
所以回答你的问题:
- 它由于Java语言规范中声明的规则而编译。
- 它被称为压倒一切。
- 我没有完整的答案,但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); } }