2012-07-19 51 views
7

我確實應該知道這一點,但出於某種原因,我不理解以下內容。Java:重寫子類中的抽象方法

我的抽象類包含下列抽象方法:

protected abstract RuleDTO createRowToBeCloned(RuleDTO ruleDTO); 

我也有另一個類,如下所示:

EvaluationRuleDTO extends from RuleDTO 

然後在我的抽象類的子類,我有以下的實現是由於「必須覆蓋或實施超類型方法」而不允許:

protected EvaluationRuleDTO createRowToBeCloned(EvaluationRuleDTO ruleDTO) { 

然而,以下是允許的:

protected EvaluationRuleDTO createRowToBeCloned(RuleDTO ruleDTO) { 

我意識到這可能是一個基本的問題,但我有點困惑。我如何才能在重寫的方法中返回RuleDTO的子類,但是我無法傳入子類?

由於

回答

11

您打破了Liskov原理:超類可以做的每件事,子類必須能夠做到。超類聲明瞭一種接受任何種類的RuleDTO的方法。但是在你的子類中,你只接受EvaluationRuleDTO的實例。如果你做了以下事情會發生什麼?

RuleDTO rule = new EvaluationRuleDTO(); 
rule.createRowToBeCloned(new RuleDTO()); 

EvaluationRuleDTO是一個RuleDTO,所以它必須滿足RuleDTO定義的契約。

但是,子類中的方法可能會返回EvaluationRuleDTO的實例而不是RuleDTO,因爲合同要返回RuleDTO,並且EvaluationRuleDTO是RuleDTO。

+0

示例很有意義,謝謝 – DJ180 2012-07-20 12:54:30

+0

發現詳細答案[here](http://stackoverflow.com/a/9421315/1276636) – Sufian 2014-10-18 13:13:51

+0

這正是我所期待的。謝謝! – 2015-06-27 15:21:16

4

這是因爲當你重寫的方法,則必須使用參數鍵入相同的類型或更一般的(較寬)型,從未較窄類型。

想想吧。如果你可以重寫一個使用更窄類型的方法,你會打破多態性能力,你不同意嗎?所以,這樣做,你會打破Liskov Substitution Principle作爲JB Nizet在他的答案中說。

+1

從你的回答引用說:「你必須爲參數利用類型相同類型或更一般的(寬)類型,從來沒有一個更小的類型」,這不適用於Java。儘管從Liskov原理的角度來看,方法參數類型在Java中必須是不變的。 – karakays 2014-10-21 08:14:07

0

Java 1.5的具有co-variant return類型的爲什麼它是有效的

子類方法的返回類型R2可以是從超類 方法的返回類型R1不同,但R 2應當是R1的子類型。即 子類可以返回的類型可能是超類返回類型的子類型。

4

Java允許覆蓋返回類型協方差,因此您可以指定覆蓋的返回類型爲更多派生類型。但是,Java不允許覆蓋參數類型協方差。

前者是安全的原因是您返回的對象至少具有派生類型較少的功能,因此依賴於該事實的客戶端仍然能夠正確使用返回的對象。

雖然這不是參數的情況。如果它是合法的,用戶可以調用抽象方法並傳入派生類型較小的類型(因爲這是在抽象類中聲明的類型),但是派生覆蓋可能會嘗試以更多派生類型的形式訪問該參數(它不是)導致錯誤。

從理論上講,Java原本已經允許參數類型禁忌 -variance,因爲這是類型安全的:如果覆蓋方法只需要一個來源的說法,你可以不小心使用的方法或不在那裏的領域。不幸的是,目前尚不可用。

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

這意味着返回類型的覆蓋方法將是重寫的方法的返回類型的子類型。

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沒有酒吧的方法。這這就是爲什麼參數不能成爲重寫方法中參數類型的子類型的原因。

在重寫方法中返回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; 
    } 
} 
相關問題