Java:覆盖子类中的抽象方法

时间:2012-07-19 21:45:40

标签: java oop

我真的应该知道这一点,但出于某种原因我不理解以下内容。

我的抽象类包含以下抽象方法:

protected abstract RuleDTO createRowToBeCloned(RuleDTO ruleDTO);

我还有另一个课程如下:

EvaluationRuleDTO extends from RuleDTO

然后在我的抽象类的子类中,我有以下实现,由于“必须覆盖或实现超类型方法”而不允许这样做:

protected EvaluationRuleDTO createRowToBeCloned(EvaluationRuleDTO ruleDTO) {

但是,允许以下内容:

protected EvaluationRuleDTO createRowToBeCloned(RuleDTO ruleDTO) {

我意识到这可能是一个基本问题,但我有点困惑。为什么我可以在重写方法中返回RuleDTO的子类,但是我不能传入子类?

由于

6 个答案:

答案 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;
    }
}