问题大纲
我正在使我当前项目基础的更好部分变得更加复杂,我有一个想法,我决定测试有关重写抽象方法。以下是我在Java中的测试类:
public abstract class Base {
public abstract <T extends Base> T test();
}
首次实施:
public class Inheritor extends Base {
@Override
public Inheritor test() {
return null;
}
}
第二次实施:
public class Inheritor2 extends Base {
@Override
public <T extends Base> T test() {
return null;
}
}
问题1
为什么要编译?我承认我很有希望它是合法的,因为它使合同不仅确保它返回一些扩展Base的东西,而且已经更加专业化了(因此我不需要在稍后某处将结果转换为我的专业类) )。
一切听起来不错,但我真的履行了基类强迫我的合同吗?我在Inheritor
中的重写实现失去了某些通用性层,不是吗?我在Inheritor
中实现此方法并不会返回Inheritor2
的实例,抽象方法似乎强制执行的可能性(因为两者都扩展为Base
)。
我想指一些文档的摘录。我的猜测是它与类型擦除有关,如果有人在他/她的答案中提到它的准确性会很好。
问题2
此程序是否有正式名称,而不是我在标题中说明的名称?
问题3
这在C#中是否可行?同事的划痕测试似乎在编译时失败了。那么通用抽象方法覆盖的方法是否存在差异?
答案 0 :(得分:3)
以下是技术细节。
关于overriding:
由类
mC
声明或继承的实例方法C
,覆盖 来自C
在课程mA
中声明的另一种方法A
,iff以下全部内容 是的:
A
是C
的超类。C
不会继承mA
。mC
的签名是mA
签名的子签名(第8.4.2节)。- 以下之一是真的:
mA
是public
。- [...]
在您的情况下,A
为Base
,C
为Inheritor
,Base#test()
为mA
,Inheritor#test()
为{ {1}}。
mC
是mC
because
方法m1的签名是a的签名的子签名 方法m2如果: - m2与m1具有相同的签名,或 - m1的签名与m2签名的删除(§4.6)相同。
mA
的删除是
mA
和public abstract Base test()
mC
是一个副标记。 What about the return type?
如果返回类型为
public Inheritor test()
的方法声明d1
覆盖或隐藏 声明另一个方法R1
,返回类型为d2
,然后R2
必须为d1
d2
或编译时错误 发生。
在return-type-substitutable
之后,我们看到了
如果R1是引用类型,则以下之一为真:
R1
可以通过未经检查的转换(第5.1.9节)转换为R2
的子类型。
Inheritor
是T extends Base
的子类型,通过未经检查的转换,所以我们都很好(尽管你应该得到编译器的警告)。
所以回答你的问题:
未经检查的转换的危险将允许您执行
class Inheritor extends Base {
@Override
public Inheritor test() {
return new Inheritor();
}
}
然后
Base ref = new Inheritor();
Inheritor2 wrong = ref.<Inheritor2>test();
会在运行时导致ClassCastException
。使用它需要您自担风险。
答案 1 :(得分:0)
我可以告诉你为什么它应该有效 - Liskov substitution principle
要问的问题是,如果将Base替换为继承者或继承者2,所有消费者是否会继续工作而不会产生负面影响?如果他们期望从test
扩展Base的任何内容,那么从使用者的角度来看,将Inheritor2与Inheritor交换,反之亦然。所以,编译器应该允许它。
您确实履行了合同,该合同规定可以返回Base
的任何子类型。任何子类型都可以是一个子类型,一个随机子类型等。
与评论员Elliott一样,我相信它只是被称为覆盖一种方法。
这里是C#中的类似实现,但在类级别上有泛型。
public abstract class Base<Type>
where Type : Base<Type>
{
public abstract Type test();
}
public class Inheritor:Base<Inheritor>
{
public override Inheritor test()
{
return null;
}
}
public class Inheritor2<Type> : Base<Type>
where Type : Base<Type>
{
public override Type test()
{
return default(Type);
}
}