2013-12-17 31 views
0

我碰到類似這樣的類。它擁有一個「與」的方法,可以讓人們把事情聯繫在一起。鏈接,返回基礎對象和類型不匹配到擴展類

public class Bear { 
    protected List<String> names = new ArrayList<String>(); 
    protected List<String> foods = new ArrayList<String>(); 

    public Bear withName(String name) { 
     names.add(name); 
     return this; 
    } 

    public Bear withFood(String food) { 
     foods.add(food); 
     return this; 
    } 
} 

// ...somewhere else 
Bear b = new Bear().withName("jake").withName("fish"); 

我發現兩個類共享相同代碼的90%。所以,我創建了一個基類之間,而「用」的方法轉移的25個左右吧,像這樣(與成員變量和所有。):

public abstract class Animal { 
    protected List<String> names = new ArrayList<String>(); 

    public Animal withName(String name) { 
     names.add(name); 
     return this; 
    } 
} 

public class Bear extends Animal { 
    protected List<String> foods = new ArrayList<String>(); 

    public Bear withFood(String food) { 
     foods.add(food); 
     return this; 
    } 
} 

然而,這現在打破一切(也有有很多地方使用這兩個類的設計)。

Bear b = new Bear().withName("jake"); // Breaks 
bear b2 = new Bear().withFood("fish"); // Fine 

錯誤給出:

類型不匹配:不能從動物轉換爲熊

顯然,當你返回基類此,它返回一個熊式,並執行不做任何形式的自動轉換。

我有哪些選擇來解決/繞過這個問題?

+0

你可以,我不清楚哪種方法工作,哪種方法沒有。 – MintyAnt

回答

2

您正在尋找的CRTP

public abstract class Animal<T extends Animal<T>> { 
    protected List<String> names = new ArrayList<String>(); 

    public T withName(String name) { 
     names.add(name); 
     return (T)this; 
    } 
} 

這會給不可避免的投選中警告,因爲類型系統無法阻止你寫class Cat extends Animal<Dog> {} class Dog extends Animal<Dog>

如果在基類中有多個構建器方法,則可以通過編寫private T returnThis() { return (T)this; }來隔離警告。

0

Bear類擴展Animal,因此繼承了作爲

public Animal withName(String name) ... 

你的方法調用是在編譯時

Bear b = new Bear().withName("jake"); // Breaks 

驗證,所有的編譯器知道是Animal#withName(String)返回其聲明的withName方法一個Animal。它不知道在運行時您實際上是否返回Bear。因此它不能讓您將該值分配給Bear

您可以執行SLaks suggests或覆蓋Bear類中的方法,並將其返回類型更改爲Bear

@Override 
public Bear withName(String name) { 
    names.add(name); // or invoke super method 
    return this; 
} 

如果您致電Bear類型的引用的方法,該方法將有Bear返回類型。 See here for why this works.