Java:重写子类中的抽象方法
我真的应该知道这一点,但出于某种原因,我不理解以下内容 My abstract类包含以下抽象方法:Java:重写子类中的抽象方法,java,oop,Java,Oop,我真的应该知道这一点,但出于某种原因,我不理解以下内容 My abstract类包含以下抽象方法: protected abstract RuleDTO createRowToBeCloned(RuleDTO ruleDTO); 我还有另一门课,如下所示: EvaluationRuleDTO extends from RuleDTO 然后,在我的抽象类的子类中,我有以下实现,这是不允许的,因为“必须重写或实现超类型方法”: 但是,允许以下情况: protected EvaluationRul
protected abstract RuleDTO createRowToBeCloned(RuleDTO ruleDTO);
我还有另一门课,如下所示:
EvaluationRuleDTO extends from RuleDTO
然后,在我的抽象类的子类中,我有以下实现,这是不允许的,因为“必须重写或实现超类型方法”:
但是,允许以下情况:
protected EvaluationRuleDTO createRowToBeCloned(RuleDTO ruleDTO) {
我意识到这可能是一个基本问题,但我有点困惑。为什么我可以在重写的方法中返回RuleDTO的子类,但不能传入子类
感谢这是因为当重写方法时,必须使用相同类型或更通用(更宽)类型作为参数类型,而不是更窄的类型 想想看。如果您可以使用更窄的类型重写方法,您会破坏多态性功能,您同意吗?因此,这样做,您将打破JB Nizet在其回答中所说的错误。Java1.5有类型,这就是它有效的原因 子类方法的返回类型R2可能不同于超类 方法的返回类型R1,但R2应该是R1的子类型。即。, 子类可以返回类型可以是超类返回类型的子类型
Java允许覆盖的返回类型协方差,因此可以将覆盖的返回类型指定为更派生的类型。但是,Java不允许覆盖的参数类型协方差 前者安全的原因是,您返回的对象至少具有派生类型较少的功能,因此依赖该事实的客户端仍然能够正确使用返回的对象 不过,这些论点并非如此。如果是合法的,用户可以调用抽象方法并传入派生程度较低的类型(因为这是在抽象类上声明的类型),但是派生重写可能会尝试以派生程度较高的类型(不是)访问参数,从而导致错误
理论上,Java可以允许参数类型相反,因为这是类型安全的:如果重写的方法只需要较少的派生参数,则不能意外地使用不存在的方法或字段。不幸的是,这目前还不可用。您正在打破Liskov原则:超类所能做的一切,子类都必须能够做到。超类声明一个接受任何类型RuleDTO的方法。但在子类中,您只接受EvaluationRuleDTO的实例。如果您执行以下操作,会发生什么情况
RuleDTO rule = new EvaluationRuleDTO();
rule.createRowToBeCloned(new RuleDTO());
EvaluationRuleDTO是RuleDTO,因此它必须履行RuleDTO定义的合同
子类中的方法可能会返回EvaluationRuleDTO的实例而不是RuleDTO,因为契约将返回RuleDTO,而EvaluationRuleDTO是RuleDTO。在早期java中不是这样,但在java 5.0中有所更改 同一类中不能有两个方法的签名仅在返回类型上不同。在J2SE5.0发布之前,类不能重写从超类继承的方法的返回类型也是事实。在本技巧中,您将了解J2SE 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;
}
}
引用您的回答“您必须使用相同类型或更通用(更广泛)的类型作为参数类型,而不是更窄的类型”,这不适用于Java。虽然从Liskov原理的角度来看这是正确的,但方法参数类型在Java中必须是不变的。这正是我所要寻找的。非常感谢。“子类中的方法可能返回EvaluationRuleDTO的实例,而不是RuleDTO”=>我在这里没有看到任何问题,这在Java中实际上是允许的。(可能不是在2012年吧?)这将很好地编译:。你几乎总是有很好的答案,但我一点也不懂。@KorayTugay我也看不出有什么问题。这就是这句话的重点:解释子类中的方法可以返回更具体的内容。
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;
}
}