问题概述
我正在扩大我当前项目基础的大部分内容,并且我有一个想法,我决定就覆盖抽象方法进行测试。这是我在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#中可能吗?同事的暂存测试似乎在编译时失败。那么通用抽象方法重写的方法是否有所不同?
什么是问题1? 2.其覆盖方法。 3.我相信艾伦·图灵说是的。
如果这被称为覆盖方法,并且上面的示例似乎未在C#中编译,则从形式上正式意味着C#不支持覆盖方法。
Java不是C#。 是的,在Java中,这是重写方法。
剩下的问题是是否有可能在C#中重现以及第一个问题(主要是关于失去通用性)
这里是技术。
关于覆盖:
An instance method mC declared in or inherited by class C, overrides
from C another method mA declared in class A, iff all of the following
are true:
A is a superclass of C.
C does not inherit mA.
The signature of mC is a subsignature (§8.4.2) of the signature of mA.
One of the following is true:
mA is public.
[...]
在您的情况下,A是Base,C是Inheritor,Base#test()是mA,Inheritor#test()是mC。
mC是mA的子签名,因为
The signature of a method m1 is a subsignature of the signature of a
method m2 if either:
- m2 has the same signature as m1, or
- the signature of m1 is the same as the erasure (§4.6) of the signature of m2.
mA的擦除是
public abstract Base test()
和mC
public Inheritor test()
是一个子签名。那返回类型呢?
If a method declaration d1 with return type R1 overrides or hides the
declaration of another method d2 with return type R2, then d1 must be
return-type-substitutable (§8.4.5) for d2, or a compile-time error
occurs.
在return-type-substitutable之后,我们看到
If R1 is a reference type then one of the following is true:
R1 can be converted to a subtype of R2 by unchecked conversion (§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。需要您自担风险使用它。
惊人的答案。 谢谢您为我指出了有关返回类型可替换性的规则,这正是我想要了解的。 同样,这是未经检查的转换,这也充分说明了IntelliJ IDEA为什么对我所做的事情感到脾气暴躁,谢谢您举一个滥用示例。 还不知道C#没有类型擦除,这就是为什么发生这种情况。 感谢您提供了确实有据可查的答案!
我可以告诉您为什么它应该起作用-Liskov替代原理
要问的问题是,如果将Base替换为Inheritor或Inheritor2,是否所有使用者都可以继续工作而不会带来负面影响?如果他们期望从test扩展Base的任何东西,那么从消费者的角度来看,将Inheritor2与Inheritor交换,反之亦然,就可以了。因此,编译器应该允许它。
您确实履行了合同,该合同规定可以返回任何Base子类型。任何子类型都可以是一个子类型,随机子类型等。
像评论员艾略特(Elliott)一样,我相信这只是所谓的覆盖方法。
这是C#中的类似实现,但在类级别具有泛型。
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);
}
}
您的C#示例将类型参数放置在类级别而不是方法级别。 我认为那有很大的不同。
确实如此。 。 。 让我看看是否可以在方法级别上解决该问题。
以为合同说:"您应该确切地归还我可以归还的东西,别无所求",这并不是因为第一个实现的通用性要差得多。 猜猜这就是混乱的来源。 我仍然对该C#版本感兴趣。