2013-04-20 181 views
19

我持有不同的Java類型的值一起製作低劣的容器對象(字符串,布爾值等)Java泛型和類型轉換

public class BadlyCreatedClass { 
    public Object get(String property) { 
     ...; 
    } 
}; 

我們以這種方式

String myStr = (String) badlyCreatedObj.get("abc"); 
Date myDate = (Date) badlyCreatedObj.get("def"); 
從中提取值

我不得不使用這個對象來編寫一些新的代碼,我想看看是否有乾淨的方法來做到這一點。更具體地說,從下面哪個方法是優選的?

明確的轉換

String myStr = (String) badlyCreatedObj.get("abc") 
Date myDate = (Date) badlyCreatedObj.get("def"); 

使用通用鑄

public <X> X genericGet(String property) { 

} 

public String getString(String property) { 
return genericGet(property); 
} 

public Date getDate(String property) { 
return genericGet(property); 
} 

使用Class.cast

<T> T get(String property, Class<T> cls) { 
    ; 
} 

我有通過幾個相關的問題上SO Java generic function: how to return Generic type走了,Java generic return type個個好像說的這種類型轉換是危險的,但我沒有看到三者之間相差太大,給你希望這個哪種方法?

感謝

回答

3

要給出一個快速回答,沒有深約好的編程實踐去...

我會用:

private <X> X genericGet(String property) { 

} 

public String getString(String property) { 
//... checks on property (String specific)... 
Object obj = genericGet(property); 
//... checks if obj is what is expected and if good return it 
return obj; 
} 

public Date getDate(String property) { 
//... checks on property (Date specific)... 
Object obj = genericGet(property); 
//... checks if obj is what is expected and if good return it 
return obj 
} 

補充通知私人genericGet。 這樣我可以檢查get屬性是我等待接收並以正確方式處理它。

中的getString取決於物業,以確保答案是一個String對象,我可以添加檢查。

我可以做其他檢查GETDATE物業,以確保它會像將返回的日期。

等等......

1

我寧願使用通用轉換。爲什麼?

  • 表達式轉換總是比較難以維護。當你閱讀代碼時,你根本不知道這個方法可能會返回什麼。更重要的是,該方法將在錯誤的方式runtimme過程中使用一些ClassCastException將ocurr的概率是相當高的。

  • 類投更是難以維持。在我看來,你用這種方式創建了一種可稱爲意大利麪代碼的東西。

  • 當您創建像getStringgetDate這樣的方法時,您可以爲班級提供非常清晰的界面。更重要的是,它總是可能得到其他類的對象比StringDate,因爲你還提供了通用的方法。

1

正如你已經提到的所有上述兩種方法是危險的,在運行時可能會導致ClassCastException秒。

如果確實有必要我寧願在「一般投」方法,因爲它的界面使得明確和自我expanatory(使genericGet私人在這種情況下)。當然,您必須爲容器中的每個class創建一個boilderplate代碼。所以'Class.cast'的優點是你不需要這些樣板方法。

結論:如果容器中有明確定義數量的類,我會使用'generic cast'。如果您需要支持類數之不盡我會用「Class.cast」

更新去:「顯式轉換」確實有一個優勢 - 來電(容器的用戶)獲取提醒那是一種類風險!

只是一個意見了......

3

就個人而言,保持許多不同的對象在一個地方,然後檢查你想回到什麼好像有點不對勁。也許你可以將持有者存儲在該BadlyCreatedClass中。

喜歡的東西:

class Holder { 
    private long id; 
    private String name; 
    private Date dateofBirth; 

    //getters and setters 
} 

然後檢索根據ID,因此不需要鑄造。

您也可以告訴我們您正在嘗試做什麼。

1

由於所有選項都涉及類型轉換,它們都以某種方式「不安全」,並可能因ClassCastExceptions而失敗。

我肯定會推薦使用像getString(),getDate()這樣的幫助器方法來處理通常存儲在這個對象中的常見類型。這三種方法對所有三個選項都很有用,因爲減少了對象用戶必須編寫的代碼。

但是,您仍然需要一種方法來從對象中接收「不常見」類型。爲此,我會選擇明確演員或者班級演員。原因是我認爲這兩種方式是最常用的方式。即使泛型方法調用可以正常工作,我認爲像obj.genericGet<String>("myproperty");這樣的方法調用並不是每個Java開發人員都知道的。這在實踐中很少見到。

即使我不喜歡將類型轉換爲對象的用戶,個人情況下也會將getObject()方法添加到幫助器方法中。這樣做的好處是您可以擁有一致的界面,並且如果我看到像getString()getDate()這樣的方法,這就是我所期望的。

3

泛型轉換方法會導致編譯器發出未經檢查的警告。未經檢查的警告表明,轉換問題在運行時未被(完全)檢查,即它可能成功,即使該值不是正確的類型。這可能會導致變量保存與其聲明類型不兼容的值,這是Java語言規範調用heap pollution的情況。

下面的程序說明了這一點:

class Holder { 
    Object value; 

    Holder(Object value) { 
     this.value = value; 
    } 

    <T> T get() { 
     return (T) value; 
    } 
} 

class C<T> { 
    T value; 

    C(Holder h) { 
     value = h.get(); 
    } 
} 

public class Test { 
    public static void main(String [] args) throws IOException { 
     Holder holder = new Holder("Hello"); 
     C<Integer> c = new C<Integer>(holder); 
     System.out.println("I just put a String into a variable of type Integer"); 

     // much later, possibly in a different part of your program 
     c.value.longValue(); // throws ClassCastException 
    } 
} 

因此,我強烈建議使用檢查轉換。普通演員(你的第一種方法)和反射演員(你的第三種方法)都會被檢查。但是,反射型投射將無法使用參數化類型(List<String>.class不能編譯...)。

因此,最簡單和最靈活的安全解決方案就是普通鑄造。