2013-10-09 205 views
1

考慮以下情形很常見的OO文獻:超類返回子類型

public class Vehicle { 
} 

public class Car extends Vehicle { 
} 

public class Bike extends Vehicle { 
} 

現在,假設我想創建一個功能get()將始終返回子類型,以便我能得到一個子類的實例:

public static void main(String[] args) { 

    Car car = Car.get(); 
    Bike bike = Bike.get(); 
    car.Start(); 
    bike.Start(); 
} 

它可以實現在意識到它的返回類型的超類public static <T> T get()沒有傳遞給它作爲一個參數或從Object鑄造?

UPDATE:

在我說的是抽象類的第一個版本。現在我刪除了摘要,並使用常規課程。

+0

我不知道Java是否支持[CRTP](http://en.wikipedia.org/wiki/Curiously_recurring_template_pattern) –

+0

爲什麼要這麼做?抽象類的全部要點是隱藏具體細節。 – dbarnes

+1

爲什麼你不說:'汽車=新車();'......等等? –

回答

1

在這種情況下,如果您在超類上調用靜態方法,Java不知道您想要返回哪種類型的子類。你提到過你不想傳入參數就可以做到,但不能。如果你能在一個參數傳遞,這是你會怎麼做:

package com.stackexchange.stackoverflow; 

public class Question19281170 { 
    public static void main(String[] args) throws InstantiationException, IllegalAccessException { 
    Car car = Vehicle.get(Car.class); 
    Bike bike = Bike.get(Bike.class); 
    car.start(); 
    bike.start(); 
    } 

    static abstract class Vehicle { 
    public void start() { 
     System.out.println(String.format("Start %s", toString())); 
    } 
    public static <T extends Vehicle> T get(Class<T> clazz) throws IllegalAccessException, InstantiationException { 
     return clazz.newInstance(); 
    } 
    @Override 
    public String toString() { 
     return "Vehicle"; 
    } 
    } 

    static class Car extends Vehicle { 
    @Override 
    public String toString() { 
     return "Car"; 
    } 
    } 

    static class Bike extends Vehicle { 
    @Override 
    public String toString() { 
     return "Bike"; 
    } 
    } 
} 

這種情況的最佳做法是隻使用多態:

Vehicle car = new Car(); 
Vehicle bike = new Bike(); 
+0

'main'中的代碼可能就是'Bike bike = Vehicle.get(Bike.class)'。 –

+0

是的,這在前面的行中說明:Car car = Vehicle.get(Car.class);我只是想演示兩種方法。事實上,如果傳遞參數是一個好的解決方案,泛型可以從超類中移除,並且可以使用具有任何通用get方法的簡單實用程序類。這似乎毫無意義。 – axiopisty

0

繼承是一種強大的功能和良好適合構建基於類的層次結構,如JavaFX中的層次結構。

但是,設計繼承類是一個痛苦和充滿潛在的問題。以下是一個常見問題:如果您決定使用HashMap來表示車輛集合,該怎麼辦?問題在於,以不打破equals()和hashcode()的契約的方式編寫子類的equals()和hashcode()方法比聽起來難。此外,繼承會破壞封裝並使您的子類依賴於超類中的實現細節,這會使代碼變得脆弱或難以維護。

繼承的替代方法是使用基於接口的類型系統,類似於Java Collections API使用的系統。

public interface Vehicle { 
    public void start(); 
    : 
} 

public class Car implements Vehicle { 
} 

public class Bike implements Vehicle { 
} 

該接口用作基本類型,具有幾個優點。最重要的是,任何實現Vehicle接口的類都可以傳遞給請求Vehicle的任何方法,而不管類是什麼。雖然這看起來類似於繼承,但沒有實際的基於類的層次結構。實現細節,封裝和equals()合同的所有問題都會消失。

到基於接口的類型系統的缺點是幾種:

  • 你必須要能夠穩健地指定的基本類型與接口定義的行爲。如果不存在這種情況,則作爲基本類型的接口變得不方便。

  • 接口必須由每個類使用它完全實現(除非您爲接口創建抽象骨架實現類,例如Collections API中的AbstractList)。

  • 它很難改變接口......但編輯超類常常是個問題。請記住,對超類的更改可能會在其子類中產生副作用,這些副作用取決於超類中的實現細節。

有一個基於接口的類型,使您的情況是這個樣子(靜態工廠使用):

Vehicle car = Car.newInstance(); 
Vehicle bike = Bike.newInstance(); 

car.start(); 
bike.start(); 

TL; DR:基於接口的類型系統是解決的問題非常有用創建一個基於類的類型系統(繼承破解封裝),並且可以成爲某些問題的很好的替代解決方案。