我真的应该知道这一点,但出于某种原因我不理解以下内容。
我的抽象类包含以下抽象方法:
protected abstract RuleDTO createRowToBeCloned(RuleDTO ruleDTO);
我还有另一个课程如下:
EvaluationRuleDTO extends from RuleDTO
然后在我的抽象类的子类中,我有以下实现,由于“必须覆盖或实现超类型方法”而不允许这样做:
protected EvaluationRuleDTO createRowToBeCloned(EvaluationRuleDTO ruleDTO) {
但是,允许以下内容:
protected EvaluationRuleDTO createRowToBeCloned(RuleDTO ruleDTO) {
我意识到这可能是一个基本问题,但我有点困惑。为什么我可以在重写方法中返回RuleDTO的子类,但是我不能传入子类?
由于
答案 0 :(得分:11)
你打破了Liskov原则:超类可以做的所有事情,子类必须能够做到。超类声明了一种接受任何类型的RuleDTO的方法。但是在您的子类中,您只接受EvaluationRuleDTO的实例。如果您执行以下操作会发生什么?
RuleDTO rule = new EvaluationRuleDTO();
rule.createRowToBeCloned(new RuleDTO());
EvaluationRuleDTO是RuleDTO,因此它必须履行RuleDTO定义的合同。
子类中的方法可能会返回EvaluationRuleDTO的实例而不是RuleDTO,因为合同是返回RuleDTO,而EvaluationRuleDTO是RuleDTO。
答案 1 :(得分:4)
这是因为当你重写一个方法时,你必须使用相同类型或更一般(更宽)类型的参数类型,而不是更窄的类型。
想一想。如果您可以使用较窄的类型覆盖方法,则会破坏多态性功能,您不同意吗?所以,按照JB Nizet的回答,你可以打破Liskov Substitution Principle。
答案 2 :(得分:4)
Java允许覆盖的返回类型协方差,因此您可以将覆盖的返回类型指定为更多派生类型。但是,Java不允许覆盖的参数类型协方差。
前者安全的原因是您返回的对象至少具有较少派生类型的功能,因此依赖该事实的客户端仍然能够正确使用返回的对象。
然而,争论的情况并非如此。如果它是合法的,用户可以调用抽象方法并传入一个较少派生的类型(因为这是在抽象类上声明的类型),但是派生的重写可能会尝试将参数作为一个更派生的类型来访问(它不是)导致错误。
理论上,Java可能允许参数类型 contra - 方差,因为这是类型安全的:如果overriden方法只需要 less - 派生的参数,你不能意外地利用那里没有的方法或领域。不幸的是,目前还没有。
答案 3 :(得分:0)
Java 1.5具有co-variant return类型,这就是它有效的原因
子类方法的返回类型R2可能与超类不同 方法的返回类型R1,但R2应该是R1的子类型。即, 子类可以返回类型可以是超类返回类型的子类型。
答案 4 :(得分:0)
在早期的Java中并非如此,但在Java 5.0中已经改变了。
同一个类中不能有两个方法,只有返回类型不同的签名。在J2SE 5.0发布之前,类也无法覆盖从超类继承的方法的返回类型。在本技巧中,您将了解J2SE 5.0中允许协变返回类型的新功能。这意味着子类中的方法可以返回一个对象,该对象的类型是由超类中具有相同签名的方法返回的类型的子类。此功能消除了过度类型检查和转换的需要。
来源:http://www.java-tips.org/java-se-tips/java.lang/covariant-return-types.html
这意味着重写方法的返回类型将是重写方法的返回类型的子类型。
答案 5 :(得分:0)
请参阅以下代码:
class A {
A foo(A a) {
return new A();
}
}
class B extends A {
@Override
// Returning a subtype in the overriding method is fine,
// but using a subtype in the argument list is NOT fine!
B foo(B b) {
b.bar();
return new B();
}
void bar() {
// B specific method!
}
}
是的,B是A,但如果有人这样做会发生什么:
B b = new B();
b.foo(new A());
A没有bar方法..这就是为什么参数不能是被覆盖的方法中参数类型的子类型。
在覆盖方法中返回A或B很好。下面的代码片段将编译并运行得很好..
class A {
A foo(A a) {
return new B(); // B IS AN A so I can return B!
}
}
class B extends A {
@Override
B foo(A b) {
return new B(); // Overridden method returns A and
// B IS AN A so I can return B!
}
public static void main(String[] args) {
A b = new B();
final A foo = b.foo(new B());
// I can even cast foo to B!
B cast = (B) foo;
}
}