簡體   English   中英

Java Generics,如何強制擴展超類的方法的兩個參數具有相同的類型?

[英]Java Generics, how to enforce two arguments of a method that extend a superclass to have a same type?

假設我有一個類層次結構如下:

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

我可以在編譯時使用泛型來實現這個目的嗎?

Ps:目前,我已實現它將拋出異常,如果兩個對象的Class不相同。 但我對此並不滿意。

提前致謝。

是的,你可以(有點)!

類型T是從參數推斷出來的,但您可以指定類型:

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()是一個靜態方法。 如果它是一個實例方法,您可以鍵入您的類並在您的方法中使用該類型。

一旦深入了解它,就會有點抽象。 您還必須為該函數提供類類型(見下文)。 如果您想強制執行此類行為,我建議您編寫單獨的方法來接受您希望比較的類型。 無論如何:

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

不知道為什么任何理智的人會想要這樣做! :)

在我看來,任何能夠接受車輛作為參數的方法都不可能接受CAR和PLANE。 由於編譯器不知道將會出現什么類型的對象(可以是CAR,BUS,PLANE),因此不能保證兩個參數類型完全相同。 如果有人擴展CAR並創建一個FORD? 兩個對象都是CAR類型。

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

嚴格地說,你的問題的答案是“不,這是不可能的”。

但是,有一種解決方法 創建方法<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

不,無論有沒有泛型都無法實現。 從本質上講,您在詢問是否可以告訴編譯器違反多態的規則。

即使你明確定義了一個沒有泛型的方法,比如下面的方法,它仍然會接受擴展Vehicle任何一對類。

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

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

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

我可以在編譯時使用泛型來實現這個目的嗎?

不是因為類型是在運行時設置的,但你可以簡單地使用內省並在函數發送無效類型時拋出異常,但如果是這樣的話那么它們可能是你設計中的一個缺陷,但這只是我的看法。

instanceof運算符將檢查底層類型是什么,因此您可以引發異常或執行更合適的操作。

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

因為你可能正在比較不同的類型,也許你不應該只有一個函數,因為它需要相當多的數量, if else在每個類中相應地實現函數可能更明智,這樣它們就可以正確地與適當的對象進行比較類型,雖然我采取了相當數量的假設,這可能是錯誤的。

不,這是不可能的!

這就是我要做的:

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)

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM