2016-07-24 81 views
7

假設我們有一個帶有compare()函數的Parent接口。Java多態:如何避免類型轉換輸入參數?

public interface Parent { 
    public int compare(Parent otherParent); 
} 

假設孩子Child1,CHILD2,Child3實現這個接口家長

public class Child1 implements Parent { 
    @Override 
    public int compare(Parent other) { 
     Child1 otherChild = (Child1)other; 
    } 
} 

此外,我使用泛型<T extends Parent>在代碼中其他任何地方。所以我需要從代碼的其他部分比較T類型的兩個對象。

我理解,這是一個不好的設計,因爲我在類型轉換compare()函數的父對象,我甚至不知道輸入的類型是否爲Child1的。

我怎樣才能避免這種類型的鑄造,而使用泛型?

回答

2

你不能。 Java接口正如其名稱所暗示的那樣 - 一種接口,用於接受所接受的類型;所以需要在編譯時確定正確的相同的方法簽名。

是,投射這種回兒童型感覺不好的設計 - 但它確實不好設計,Java的規定,所以這不是你的錯。

+0

猜猜我將不得不使用類型轉換即可。謝謝! –

3

答案取決於你如何對待你的繼承層次:

  • 如果派生類必須比較只有自己的類的實例,並允許在與不同的類的對象呈現給拋出一個錯誤,使用投,但檢查instanceof
  • 如果派生類必須跨越類型邊界比較保護它,需要更復雜的策略,以避免鑄造:你可以使用一個訪問者狀的圖案,以實現執行比較所需的雙重分派。

下面是使用抽象類的實現:

// Use this class as the base class of your Child classes 
class AbstractChild implements Parent { 
    @Override 
    public int compare(Parent otherObj) { 
     if (!()) { 
      throw new IllegalStateException("Unexpected implementation of Child"); 
     } 
     AbstractChild other = (AbstractChild)otherObj; 
     return doCompare(other); 
    } 
    protected abstract int doCompare(AbstractChild other); 
    protected abstract int accept(Child1 c1); 
    protected abstract int accept(Child2 c2); 
    protected abstract int accept(Child3 c3); 
} 
class Child1 extends AbstractChild { 
    @Override 
    protected int doCompare(AbstractChild other) { 
     return other.accept(this); 
    } 
    @Override 
    protected int accept(Child1 c1) { 
     // Compare c1 instance to this object 
    } 
    @Override 
    protected int accept(Child2 c2) { 
     // Compare c2 instance to this object 
    } 
    @Override 
    protected int accept(Child3 c3) { 
     // Compare c3 instance to this object 
    } 
} 
class Child2 extends AbstractChild { 
    @Override 
    protected int doCompare(AbstractChild other) { 
     return other.accept(this); 
    } 
    @Override 
    protected int accept(Child1 c1) { 
     // Compare c1 instance to this object 
    } 
    @Override 
    protected int accept(Child2 c2) { 
     // Compare c2 instance to this object 
    } 
    @Override 
    protected int accept(Child3 c3) { 
     // Compare c3 instance to this object 
    } 
} 
class Child3 extends AbstractChild { 
    @Override 
    protected int doCompare(AbstractChild other) { 
     return other.accept(this); 
    } 
    @Override 
    protected int accept(Child1 c1) { 
     // Compare c1 instance to this object 
    } 
    @Override 
    protected int accept(Child2 c2) { 
     // Compare c2 instance to this object 
    } 
    @Override 
    protected int accept(Child3 c3) { 
     // Compare c3 instance to this object 
    } 
} 

在這種方法唯一的轉換是在抽象類級別。實際比較邏輯的實現包含在accept方法中,其中在已知類型的兩個對象之間進行比較。

+0

我必須承認,與使用in-method類型的castin /檢查相比,我對此代碼的優勢感到有些喪失。在過去的十年中,我並沒有過多地使用Java專家(從那時起,Java 1.4出現了),所以:讓JVM進行必要的類型比較,以便將任何性能優點「委託」到正確的方法或者有其他優點(除了可能是「正確的做法」,讓強類型語言處理類型消歧)? –

+0

@MarcusMüller這是對「你如何避免施放」這個問題的回答,它假設你想避免它出於某種原因。過去我在非常特殊的情況下使用了類似的方法,當時我可以利用訪問者進行比較。在Java-5推出時,我已經從Java過渡到了C#,並且我使用了不同的語言機制來實現我的多次調度(即自C#4起可用的'dynamic')。 – dasblinkenlight

+0

同意我的評論有點偏離主題:)顯然,訪問者模式是恢復類型信息丟失的傳統方法,它是通過以多態語言傳遞信息而丟失的,這種語言在乾淨地處理不同類型的信息對象時受到限制將Java與C#進行比較,或者甚至是像Python這樣的動態類型化腳本語言,如果我想的話,我實際上可以使用類型作爲可調用字典的關鍵字)。我的希望僅僅是,你可以在這方面閃耀一點經驗,謝謝! –

2

你無法避免它在一般情況下。然而,如果子類的數量是固定的,你可以申請一個類似於訪問者模式,以避免鑄件在寫更多的代碼的代價:

interface Parent { 
    int compare(Parent p); 
    protected int compareWith(Child1 c); 
    protected int compareWith(Child2 c); 
    protected int compareWith(Child3 c); 
} 

class Child1 implements Parent { 
    @Override int compare(Parent p) { 
     return p.compareWith(this); 
    } 
    @Override int compareWith(Child1 c) { 
     //specific code to child1 
    } 
    @Override int compareWith(Child2 c) { 
     //specific code to child2 
    } 
    @Override int compareWith(Child3 c) { 
     //specific code to child3 
    } 
} 
// and so on for the other children 

這並避免鑄造,但我不知道在這裏介紹的情況下,額外的努力是值得的。

5

爲什麼不呢?

interface Parent<T extends Parent<?>> { 
    int compare(T type); 
} 

class Child1 implements Parent<Child1>{ 

    @Override 
    public int compare(Child1 type) { 
     return 0; 
    } 
} 

編輯:爲了確保正確使用,您可以使用

interface Parent<T extends Parent<T>>{ /* ... */ } //instead of wildcard 

不過說實話那「環」看起來並不漂亮,因爲泛型在Java中沒有在運行時工作(more information),它們本質上是語法糖,因爲你稱之爲「壞設計」的同一個角色,所以我認爲你目前的方法不好。

+1

'延伸Parent '部分確實看起來不漂亮。但事實上,它可以被忽略 - 假設實現者負責使用正確的'T'。 「Comparable」也是這樣。 – Marco13

2

您在那裏描述的模式有,正好是模式Comparable。實際上,您應該考慮省略Parent接口,並將其替換爲Comparable

Comparable接口和它的用途也表明這可怎麼解決:該類型可以作爲參數,以確保只有匹配的類型可以傳遞給compare方法:

interface Parent<T> { 
    int compare(T that); 
} 

class Child1 implements Parent<Child1> { 
    @Override 
    public int compare(Child1 that) { 
     ... 
    } 
} 

對於所有其他情況下,你至少也得想想,當不同Child - 班被相互比較應該發生什麼:

Child1 c1 = new Child1(); 
Child2 c2 = new Child2(); 

// Should this be possible? What should happen here? 
// How should different child classes be compared? 
c1.compare(c2);