2014-12-02 39 views
3

我有一個enum存儲爲一個數據庫表中的一些元數據:Java:如何實現<??擴展...>至<E>

static enum DataTable implements TableMetaData { 

    HISTORIES ("h", Hx.class, HxColumn.HPI_ID), 
    : 
    : 

    private final String        e_alias; 
    private final Class<? extends Row>    e_tbl; 
    private final ColumnMetaData      e_idCol; 

    DataTable(String         al, 
      Class   <? extends Row>    c1, 
      ColumnMetaData        id) 
    {...} 

    @Override public Class<? extends Row> modelClass() { return this.e_tbl; } 
    : 
    : 
} 

感興趣的領域是e_tbl存儲對應於數據庫表中的模型對象類。該項目中的所有模型對象類都實現了一個接口。我希望能夠訪問此字段以生成此類的實例。

我的問題是,我使用了一個未檢查的轉換,我不知道是否安全,我還沒有弄清楚如何執行運行時檢查以確保模型對象匹配。這裏是演員陣容的代碼:

static final <E extends Row> List<E> doQuery (
      final List<JdbcValue> pars, 
      final String sql, 
      final TableMetaData dm 
    ) 
{ 

    List<E> result = new ArrayList<E>(); 

    @SuppressWarnings("unchecked") 
    Class<E> cls = (Class<E>) dm.modelClass(); 
    : 
    : 
} 

我相信這是一個危險演員。該查詢旨在生成E實例,並且將使用所提供的枚舉常量中包含的模型類來創建這些實例。問題是我不知道E與枚舉中存儲的類標記相匹配。這是Class.asSubclass()似乎無法幫助我的情況。

問題是我有一個字段是<? extends Row>,我需要使它與<E extends Row>兼容。

一個解決方案是重構該方法,它需要參數Class<E> cls而不是枚舉。這將提供至少一些編譯時保證,類標記適用於正在生成的結果列表的類型。但是,這仍然不能讓我使用枚舉中的數據;在方法調用之前,我仍然必須將其投射出去。

有沒有可以使用的支票?

============================================== ====

修訂14年12月6日

在我看來,這是一個問題,如果沒有良好的,乾淨的解決方案。

枚舉不支持泛型類型參數(它們不能)。

每次嘗試將參數化類型存儲在枚舉常量中時,編譯時都會出現不兼容的類型信息。

建議使用參數化的類型安全單例模式,使編譯器能夠檢查類型安全性,但是我擁有的API被這些枚舉類所滲透,我不覺得我可以將它們重構爲常規類。

爲了限制損壞,不知下面的推理是正確的:

  • 與枚舉常數相關聯的數據是靜態的和最終;在這裏它也是一成不變的。

  • 我完全控制該進入枚舉常量的數據,所以我知道在運行時,該e_tbl場總是會有些類型的擴展的Class對象。

這些假設承認兩種方法,我認爲它們有相同的缺陷。在枚舉類,存取方法被改寫爲:

@Override public <E extends Row> Class<E> modelclass() { 
    @SuppressWarning("unchecked") 
    Class<E> cls = (Class<E>) this.e_tbl; 
    return this.e_tbl; } 
  1. 我可以將數據存儲到枚舉到使用原始類型的字段。原始類型與通用訪問器方法互操作,因此它會編譯但會生成警告。

  2. 我可以將數據存儲到枚舉到使用<? extends Row>通配符類型的字段。這不會使用通用訪問器方法(<E> extends Row>)進行編譯,因此我必須使用未經檢查的強制轉換。警告仍然生成。

因爲我知道枚舉數據總是延伸,我認爲這可能是好的。我不能做的是保證正在檢索的枚舉數據適合生成的List類型。但我不認爲除了記錄API用戶必須爲正在返回的查詢發送正確的TableMetaData常量或結果將是「未定義」之外,沒有其他方法可以控制這一點。

由於枚舉不適用於泛型類型,我不認爲有更好的解決方案。

+0

很對。不過,enum中的數據(它被指定爲提供模型對象類類型標記)應該可以以這種方式使用,這似乎也是「正確的」。如果我爲參數手動輸入'Hx.class',而不是使用'dm.modelClass()',即使它們是相同的,我也可以讓所有的東西都可以工作。 – scottb 2014-12-02 18:48:51

+0

所有這些通用類型在運行時不存在。所以在這一點上什麼都不應該發生,甚至可能不會晚。 (轉換成功後,請參閱http://ideone.com/B3nTcy崩潰)。你需要一個具體的東西來與運行時進行比較,那些'E'不再存在了。 – zapl 2014-12-02 19:17:32

回答

2

有一兩件事你可以做:

  • 打開TableMetaDataTableMetaData<E>並在E
  • 方面擺脫enum的定義類(因爲不支持類型參數),寫一個類 每ENUM註冊

所以界面看起來大致如下

class Histories implements TableMetaData<SpecialRowType> { 
    Class<SpecialRowType> modelClass(); 
} 

哪個應該最終讓你用劇組使用它。

static final <E extends Row> List<E> doQuery (
      final List<JdbcValue> pars, 
      final String sql, 
      final TableMetaData<E> dm 
    ) 
{ 

    List<E> result = new ArrayList<E>(); 
    // type safe, because E refers to the same thing. 
    Class<E> cls = dm.modelClass(); 
    : 
} 

現在,編譯器可以確保? extends Row類是一樣的E extends Row類,它確實有一個共同的祖先,但這並不意味着你可以將它們轉換成對方。

相關問題