使用Java通配符 [英] Using Java wildcards

查看:115
本文介绍了使用Java通配符的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

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

I want to implement some kind of component system in Java.

有一个名为Form的接口

There is an interface, called Form

interface Form<T> {
    T getObject();

    // ...
}

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

And I'd like to provide some abstract class called CompoundForm to assist building complex forms from simple forms.

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

User of CompoundForm needs to provide some description of each component using Component interface

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实现类似于:

Given this interface CompoundForm implementation is something like:

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;
    }
}

是否有可能在类型中实现类似的东西 - 安全的方式没有未经检查的演员阵容?我对通配符的使用是否正确?

Is it possible to implement something like this in type-safe manner without unchecked casts? Is my usage of wildcards correct?

推荐答案

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

Intuitively your code makes perfect sense; however a limitation in Java type system makes it illegal. Let's see a simpler example first

<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是固定的,尽管是未知的类型。这称为通配符捕获。将列表< X> 传递给 f1 编译,编译器推断出T = X.

When compiling f1(a), compiler internally treats the type of a as List<X>, where X is a fixed albeit unknown type. This is called "wildcard capture". Passing a List<X> to f1 compiles, compiler infers that T=X.

编译 f2(a,a)时,类似的事情发生了;但是,通配符捕获分别应用于两次 a ,导致第一个 a 类型为列表< X1> ,以及列表< X2> 的第二个 a 。编译器不会分析 a 保持不变,因此 X1 = X2 。没有这些知识,将列表< X1> 列表< X2> 传递给 f2() 无法编译。

When compiling f2(a,a), similar thing happens; however, wildcard capture is applied on two occurrences of a seperately, resulting the 1st a being of type List<X1>, and the 2nd a of List<X2>. Compiler doesn't not analyze that a remain unchanged therefore X1=X2. Without that knowledge, passing List<X1> and List<X2> to f2() does not compile.

解决方法是让 a 只出现一次:

The workaround is to make a appear only once:

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){ ... }

返回在你的情况下,你也需要一个辅助方法:

Back to your case, you need a helper method too:

<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类型系统中表达,但它由代码逻辑保证。您可以合法地禁止警告,因为您已经检查以确保演员必须在运行时工作。

For the next problem, you need a cast. There is no other way to tell compiler that the component and the form share the same U. That relation cannot be expressed in Java type system, but it is guaranteed by your code logic. You can legitimately suppress the warning, because you have "checked" to make sure that the cast must work at runtime.

这篇关于使用Java通配符的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆