2012-06-19 40 views
9

假設我有一個類層次結構如下:Java泛型,如何強制擴展超類具有相同類型的方法的兩個參數?

class Vehicle; 
class Car extends Vehicle; 
class Plane extends Vehicle; 

我有一個函數,其中兩個對象

public <T extends Vehicle> generateDiff(T original, T copy) 

比較在編譯時,上述方法保證了兩個對象是Vehicle,但它無法確定兩個對象的類型是否相同。

generateDiff(new Car(), new Car()); //OK 
generateDiff(new Plane(), new Plane()); //OK 
generateDiff(new Car(), new Plane()); //WRONG 

我可以在編譯時使用泛型來實現這個嗎? P:目前,我已經實現了它,如果兩個對象的Class不相同,它將拋出異常。但我對此並不滿意。

在此先感謝。

回答

8

是的,你可以(種)!

類型T正在從參數inferred,但您可以指定類型:

MyClass.<Car>generateDiff(new Car(), new Plane()); // generates a compile error 

沒有打字方法,類型T被推斷爲滿足邊界所使用的最窄類,所以對於參數CarPlane,將工作最窄類型爲Vehicle,所以這兩條線是等效的:

generateDiff(new Car(), new Plane()); // type is inferred as Vehicle 
MyClass.<Vehicle>generateDiff(new Car(), new Plane()); 

上面的代碼假定generateDiff()是一種靜態方法。如果它是一個實例方法,你可以鍵入你的類並在你的方法中使用該類型。

+0

+1推斷的類型! – UmNyobe

3

我認爲不可能,任何可以接受Vehicle作爲參數的方法都可以接受CAR和PLANE。由於編譯器不知道會出現什麼樣的對象(可以是CAR,BUS,PLANE),它不能保證兩個參數的類型完全相同。如果有人擴展CAR並創建FORD?這兩個對象都是CAR類型。

只有這樣才能確保這是在運行時使用自定義邏輯。

1

不,這不能通過泛型來實現。從本質上講,你在問是否可以告訴編譯器違反多態性規則。

即使您明確定義了一個沒有泛型的方法(如下面的方法),它仍然會接受任何延伸爲Vehicle的類對。

void generateDiff(Vehicle maybePlane, Vehicle maybeCar) { ... 

有一種情況是例外,但我不會推薦它。如果您正在調用final類對象(或任何未擴展的類)的方法,並且這些對象可以擴展爲Vehicle,則可以覆蓋該方法以匹配每個參數的該類簽名。但是你需要明確地定義每一個。

class Vehicle; 
final class Car extends Vehicle; 
final class Plane extends Vehicle; 

void generateDiff(Car car1, Car car2) { ... 
void generateDiff(Plane plane1, Plane plane2) { ... 

generateDiff(new Car(), new Car()); // OK 
generateDiff(new Plane(), new Plane()); // OK 
generateDiff(new Car(), new Plane()); // No matching method 
1

可以使用泛型我實現這一目標在編譯時?

都能跟得上,因爲類型是在運行時設置的,但你可以簡單地使用內省,並拋出一個異常,如果無效類型發送到功能,但如果多數民衆贊成的話,那麼他們也許在你的設計那裏有一個缺陷, 但那只是我的個人意見。

instanceof運算符會檢查底層的類型,所以你可以引發一個異常或者做一些更合適的事情。

public <T extends Vehicle> generateDiff(T original, T copy) 

,因爲你可能會比較不同類型,也許你不應該有一個單一的功能,因爲它需要的if else相當數量的,可能是明智的相應實現每個類的功能,使他們能夠正確地與適當類型的對象進行比較,儘管我採取了相當數量的假設,這可能是錯誤的。

6

一旦你深入瞭解它,它會變得有點抽象。你將不得不爲該函數提供類的類型(見下文)。如果你想強制執行這種行爲,我會建議編寫單獨的方法來接受你想要比較的類型。總之:

public <C extends Vehicle> void generateDiff(Class<C> type, C original, C copy); 

而且你可以使用它作爲這樣的:

generateDiff(Plane.class, new Plane(), new Plane()); // OK 
generateDiff(Car.class, new Car(), new Car()); // OK 
generateDiff(Plane.class, new Plane(), new Car()); // ERROR 
generateDiff(Vehicle.class, new Plane(), new Car()); // OK 

不知道爲什麼任何理智的人會想,雖然這樣做! :)

+0

+1對'不知道爲什麼任何理智的人會想這樣做,但! :)' –

0

不,這是不可能的!

這是我會做:

class Vehicle; 
class Car extends Vehicle; 
class Plane extends Vehicle; 

class DiffGenerator { 
    public Diff generateDiff(Car original, Car copy) { 
    return generateDiff(original, copy) 
    } 

    public Diff generateDiff(Plane original, Plane copy) { 
    return generateDiff(original, copy) 
    } 

    private Diff generateDiff(Vehicle original, Vehicle copy) { 
    return generatedDiff; 
    } 

} 

注意的私有方法? private Diff generateDiff(Vehicle original, Vehicle copy)

3

嚴格地說,對於你的問題的答案是'不,這是不可能的'。

但是,有一個workaround。創建一個方法<T extends Vehicle> VehicleDiffer<T> compare(T vehicleA)其中VehicleDiffer<T>有一個方法ReturnType with(T vehicleB)。現在,你可以做以下電話:

compare(new Car()).with(new Car()); // okay 
compare(new Plane()).with(new Plane()); // okay 

下會失敗:

compare(new Car()).with(new Plane()); // with(Car) can't be called with argument type Plane 
+0

天才的想法我說。 – Saintali

相關問題