2017-09-18 61 views
6

爲什麼在這個通用接口中需要不安全的cast (T)?如果T媲美本身,即實現ExtendedComparable<super of T>這意味着還ExtendedComparable<T>,那麼爲什麼類型擦除需要ExtendedComparable<T>定投至T?如何避免Java泛型擴展Comparable接口中未經檢查的強制轉換?

/* @param <T> T must be comparable to itself or any of its superclass 
* (comparables are consumers, thus acc. to the PECS principle 
* = producer-extends,consumer-super we use the bounded wildcard type "super") 
*/ 
public interface ExtendedComparable<T extends ExtendedComparable<? super T>> { 
    Comparator<? super T> getComparator(); 
    default boolean greaterThen(T toCompare) { 
     return getComparator().compare((T) this, toCompare) > 0; 
    } 
} 
+0

這篇文章可以爲您的幫助:https://stackoverflow.com/a/25783345/4867374 –

回答

5

因爲沒有保證this實際上是T類的一個實例,甚至擴展。

例如考慮這個問題:T0 extends ExtendComparable<T0>和T0爲T0的超:

public class T0 implements ExtendComparable<T0> {...} 
public class T1 implements ExtendComparable<T0> {...} 

T0,因爲它與結合的符合規定的罰款。在這種情況下,thisT0的一個實例,所以你很好;鑄造(T)this(因此(T0)this)是有道理的。

隨着T1因爲結合應用到T0沒有T1聲明也正確,T是取代T0。然而thisT1T1是不是超級也不是T0一個孩子。是的,兩個都執行 ExtendedCompatible<T0>,但你不能在兄弟姐妹之間施放。例如Integer和Double擴展數字,但(Integer) new Double(0.0)失敗。 鑄造(T)也轉換爲(T0)失敗。

您正在做的假設是T將被設置爲與已聲明的類相同,並且當前無法強制這些語義。我希望這會在未來的Java語言版本中的某個時候發生變化,但也許爲什麼Java語言「特別工作組」會避免這樣做是有原因的。

還有就是要完全避免投的方式,但更好,當你做出一個ExtendedCompatible抽象類,而非接口。

你可以聲明T類型的值將被保護的構造通過擴展類設置的最後一個字段而這又必須通過this作爲其值:

public abstract class ExtendedCompatible<T extends ExtendedCompatible<? super T>> { 
    private final T thiz; 

    protected ExtendedCompatible(final T thiz) { 
    if (this != thiz) throw new IllegalArgumentException("you must pass yourself"); 
    this.thiz = thiz; 
    } 
    ... 

    public class MyExtendedCompatible extends ExtendedCompatible<MyExtendedCompatible> { 
    public MyExtendedCompatible() { 
      super(this); 
    } 
    } 

你付出的代價是額外的內存對自身有愚蠢的引用的消費以及將this傳遞給父構造函數的附加代碼/ CPU負擔。

另一個辦法是宣佈一個抽象的方法來獲得T(本):

// Parent abstract class: 
    protected abstract T getThiz(); 
// Child class... for each class: 
    protected MyChildClass getThiz() { return this; } 
+1

that'a IMO,希望能非常詳細和很好的答案。堆棧溢出有時僅授予最高評分用戶的聲譽,但這是一個非常好的選擇。 +1 – Eugene

+0

謝謝。那是對的。我們打算把這個接口用於許多枚舉。但是,不幸的是,提議的解決方法不適合枚舉,因爲它們不支持繼承,因此不能從抽象類派生。第二個解決方法也不太可行,因爲接口中的每個方法都需要檢查getThiz()!= this(在Java 9中,至少所有這些相同的檢查都可以重構爲私有接口方法),並且每個枚舉都會需要實現getThiz()。 – ms34449

+0

@ ms34449枚舉麼?我認爲你可以在每個常量中重寫方法,在你的聲明中包含一個主體。編輯啊!你的意思是跨越枚舉而不是跨越常量。 –

0

感謝。瓦倫丁是對的。即使這兩種類型都實現了相同的界面,但這並不是也不應該在它們之間進行強制轉換。是的,Java中沒有任何機制強制將T傳入與正在聲明的類相同的類。

+2

這應該是對瓦倫丁回答的迴應 - 你應該接受他的可能性 – Eugene

相關問題