使用Java通配符

我想在Java中实现某种组件系统。

有一个名为Form的界面

interface Form { T getObject(); // ... } 

我想提供一些名为CompoundForm的抽象类来帮助从简单forms构建复杂的表单。

CompoundForm的用户需要使用Component接口提供每个组件的一些描述

 interface Component { /** Factory method to build new form for given component */ Form 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 implements Form { /** User should override this method and provide a collection of * actual components of different types, hence ? wildcard */ protected abstract Map<String, Component> 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> e: componentMap()) { String subPrefix = e.getKey(); Component 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> e: componentMap()) { String subPrefix = e.getKey(); Component component = e.getValue(); Form form = formMap.get(subPrefix); // !!! Compile error here: type error result = component.set(result, form.getObject()); } return result; } } 

是否可以在没有未经检查的强制转换的情况下以类型安全的方式实现这样的东西? 我对通配符的使用是否正确?

直观地说,您的代码非常有意义; 但Java类型系统的限制使其成为非法。 让我们先看一个更简单的例子

  void f1(List a){ ... }  void f2(List a1, List a2){ ... } List a = ...; f1(a); // compiles f2(a, a); // does not compile 

在编译f1(a) ,编译器在内部将a的类型视为List ,其中X是固定的虽然未知类型。 这称为“通配符捕获”。 将List传递给f1编译,编译器推断出T = X.

编译f2(a,a) ,会发生类似的事情; 然而,通配符捕获分别应用于两次出现,导致第aList类型,第二aList 。 编译器不会分析保持不变因此X1=X2 。 没有这些知识,将ListList传递给f2()不会编译。

解决方法是只出现一次:

 List a = ...; f2_1(a); // compiles  void f2_1(List a){ f2_2(a,a); } // compiles, two a's same type: List  void f2_2(List a1, List a2){ ... } 

回到你的情况,你也需要一个帮助方法:

  Form createForm(Component component, T object, String prefix) { return component.createForm(component.get(object), prefix + subPrefix); } -- Component component = e.getValue(); Form form = createForm(component, object, prefix + subPrefix); 

对于下一个问题,您需要一个演员表。 没有其他方法可以告诉编译器组件和表单共享相同的U 这种关系不能在Java类型系统中表达,但它由代码逻辑保证。 您可以合法地禁止警告,因为您已“检查”以确保强制转换必须在运行时工作。

看看复合模式 。 然后,如果您认为使用generics对您的问题很有用,那就去阅读一本很好的教程,比如这个 。