2011-09-20 87 views
4

我想在Java中實現某種組件系統。使用Java通配符

有一個接口,稱爲表

interface Form<T> { 
    T getObject(); 

    // ... 
} 

,我想提供一些所謂的CompoundForm協助從簡單的形式構建複雜的形式抽象類。 CompoundForm的

用戶需要使用組件的接口來提供每個組件的一些描述

interface Component<T, U> { 
    /** Factory method to build new form for given component */ 
    Form<U> createForm(U u, String prefix); 

    /** Extract component of type U from the compound t */ 
    U get(T t); 

    /** Mutate t or build new compound of type T using information from u */ 
    T set(T t, U u); 
} 

鑑於此接口CompoundForm實現是一樣的東西:

abstract class CompoundForm<T> implements Form<T> { 
    /** User should override this method and provide a collection of 
     * actual components of different types, hence ? wildcard */ 
    protected abstract Map<String, Component<T, ?>> componentMap(); 

    private Map<String, Form<?>> formMap = new TreeMap<String, Form<?>>(); 
    private final T object; 

    public CompoundForm(T object, String prefix) { 
     this.object = object; 
     for (Entry<String, Component<T, ?>> e: componentMap()) { 
      String subPrefix = e.getKey(); 
      Component<T, ?> component = e.getValue(); 

      // !!! Compile error here: type error 
      Form<?> form = component.createForm(component.get(object), prefix + subPrefix); 
      formMap.put(subPrefix, form); 
     } 
    } 

    public T getObject() { 
     T result = object; 
     for (Entry<String, Component<T, ?>> e: componentMap()) { 
      String subPrefix = e.getKey(); 
      Component<T, ?> component = e.getValue(); 
      Form<?> form = formMap.get(subPrefix); 

      // !!! Compile error here: type error 
      result = component.set(result, form.getObject()); 
     } 
     return result; 
    } 
} 

是否有可能實現這樣的事情在類型安全的方式沒有未經檢查的強制轉換?我的通配符的用法是否正確?

回答

3

直觀上你的代碼非常有意義;然而Java類型系統中的限制使其非法。讓我們看一個簡單的例子第一

<T> void f1(List<T> a){ ... } 

<T> void f2(List<T> a1, List<T> a2){ ... } 

List<?> a = ...; 

f1(a); // compiles 

f2(a, a); // does not compile 

當編譯f1(a),編譯器在內部對待的a類型爲List<X>,其中X是一個固定儘管未知類型。這被稱爲「通配符捕獲」。通過List<X>f1編譯,編譯器推斷T = X。

當編譯f2(a,a)時,會發生類似的事情;然而,通配符捕獲單獨應用於兩次出現的a,導致第一個aList<X1>類型,第二個aList<X2>。編譯器不會分析,因此a保持不變,因此X1=X2。沒有這些知識,傳遞List<X1>List<X2>f2()不會編譯。

的解決辦法是讓a只出現一次:

List<?> a = ...; 

f2_1(a); // compiles 

<T> void f2_1(List<T> a){ f2_2(a,a); } // compiles, two a's same type: List<T> 

<T> void f2_2(List<T> a1, List<T> a2){ ... } 

返回到你的情況,你需要一個輔助方法太:

<T, U> Form<U> createForm(Component<T, U> component, T object, String prefix) 
{ 
    return component.createForm(component.get(object), prefix + subPrefix); 
} 

-- 
    Component<T, ?> component = e.getValue(); 

    Form<?> form = createForm(component, object, prefix + subPrefix); 

對於下一個問題,你需要一個演員。沒有其他方式告訴編譯器該組件和表單共享U。這種關係不能用Java類型系統來表達,但它是由你的代碼邏輯保證的。您可以合法地禁止警告,因爲您已經「檢查」以確保演員必須在運行時工作。

0

看一看Composite Pattern。然後,如果您認爲使用泛型對您的問題有用,請閱讀一個很好的教程,例如this one