【问题标题】:How do I declare a member with multiple generic types that have non-trivial relationships?如何声明具有多个具有非平凡关系的泛型类型的成员?
【发布时间】:2011-01-18 02:27:28
【问题描述】:

这是我想在我的 java 代码中写的内容:

private <A extends Action<R>, R extends Result> MyType<A,R> member;

然而,这是无效的语法。所以我最终写了:

private MyType<? extends Action<? extends Result>, ? extends Result> member;

但这忽略了从Result 派生的两个类是相同的事实。我的类方法都强制执行这种关系,所以我可以确定 MyType 强制执行它,但在某些情况下我仍然必须不安全地类型转换 member

更多详情

这是我想要做的精确版本,虽然它更严格:

我希望我能做到:

private <A extends Action<R>, R extends Result> 
    Map< Class<A>, ActionHandler<A,R> > handlers;

相反,我必须这样做:

private Map< Class< ? extends Action<? extends Result> >, 
             ActionHandler<? extends Action<? extends Result>, 
                           ? extends Result> > handlers;

我的方法强制执行所需的关系,如下所示:

public <A extends Action<R>, R extends Result> void addHandler( 
    ActionHandler<A, R> handler ) {
  handlers.put( handler.getActionType(), handler );
}

我想要以下方法:

public <A extends Action<R>, R extends Result> ActionHandler<A, R> 
    findHandler( A action ) {
  return handlers.get( action.getClass() );
}

但这不起作用:我必须添加演员表和@SuppressWarnings("unchecked")

我尝试过的事情

我尝试为此目的创建一个新类:

public class MyMap <A extends Action<R>, R extends Result> extends 
    HashMap< Class<A>, ActionHandler<A,R> > { ... }

MyMap< ?, ? > handlers;

但它不起作用,我仍然需要演员表。

【问题讨论】:

  • 你不能对这样的类型进行参数化。事件 private &lt;T&gt; List&lt;T&gt; ts; 无效。您能更清楚地了解您正在尝试做什么吗?像“Map&lt;T, Class&lt;T&gt;&gt;”这样的东西,其中T对于每个条目来说都是一样的?
  • 我知道我不能这样做,使用那个(无效的)语法更容易解释。我已经添加了详细信息。
  • 完全正确:我希望 T 是通用的,但关系应该适用于地图中的每个条目。
  • 有趣的是,public class Test&lt;T, A&gt; where A : EventArgs where T : TestArgs&lt;A&gt; 在 C# 中运行良好。尽管语法看起来几乎完全相同,但完全不同的泛型实现是耶!
  • @Tanzelax 这也适用于 Java(我已经更新了我的问题以显示示例)。问题在于声明一个变量,该变量想要强制泛型之间的关系。

标签: java generics


【解决方案1】:

没有干净的方法可以做到这一点。您可以做的最好的事情就是您已经完成的工作 - 将 Map 隐藏在强制类型安全的方法后面,并在这些方法中隐藏必要的强制转换。

来自 Effective Java 第 2 版,第 29 项:“考虑类型安全的异构容器”(Josh Bloch 在其中勾勒出您正在做的事情的一个更简单的版本,一个键所在的“地图”类和值是关键类的实例):

接下来要注意的是favorites Map 的值类型就是Object。换句话说,Map 不保证键和值之间的类型关系。事实上,Java 的类型系统并不足以表达这一点。但我们知道这是真的,我们会在检索收藏夹时利用它。

鉴于无论如何您都无法对值进行任何有用的类型强制,最不混乱的实现可能是这样的:

public class HandlerRegistry
{
    private Map<Class<?>, Object> map = new HashMap<Class<?>, Object>();

    public <R extends Result, A extends Action<R>>
        void addHandler(Class<A> actionClass, ActionHandler<R, A> handler) {
        map.put(actionClass, handler);
    }

    @SuppressWarnings("unchecked")
    public <R extends Result, A extends Action<R>> ActionHandler<R, A>
        findHandler(A action) {
            return (ActionHandler<R, A>) map.get(action.getClass());
    }
}

关于基于Map 的解决方案需要注意的一点:如果action 不完全是用作键的类,map.get() 将不起作用——例如,如果它是一个子类,或者如果关键类是一个接口,action 本身就是实现。你可能会更好:

    @SuppressWarnings("unchecked")
    public <R extends Result, A extends Action<R>> ActionHandler<R, ? super A>
        findHandler( A action ) {
            for ( Map.Entry<Class<?>, Object> entry : map.entrySet() )
            {
                if (entry.getKey().isAssignableFrom(action.getClass())) {
                    return (ActionHandler<R, ? super A>) entry.getValue();
                }
            }
            return null;
    }

(注意:原来上面的第二个例子返回了ActionHandler&lt;R, A&gt;,实际上并不正确——应该是? super A。(我们不知道它除了@还能处理什么987654333@——它可以是A的任何超类,一直到Object。)在这种特殊情况下,它可能足够安全,但如果你考虑像List这样的东西,我们可能会有很多麻烦:你可以安全地将A 放入List&lt;? super A&gt;,但如果你假设你只会得到As,你会得到ClassCastExceptions。)

【讨论】:

  • 非常详细的答案!谢谢!
【解决方案2】:

所以你想实现一个类型安全的异构容器而不进行强制转换。

有趣的谜题。

我认为这在 Java 中是不可能的。考虑:

class Entry<K,V> { 
    K key;
    V value;
}
interface Map<E extends Entry<?,?>> { 
    void put(E e);
    E get(Object k);
}

class MyEntry<T> extends Entry<Class<T>, T> { }

持有Map&lt;MyEntry&lt;?&gt;&gt; 的调用者现在可以确定条目是类型安全的,而Map 中指定的put 方法很好地执行了这一点。此外,get 方法保证返回正确的映射。我无法表达的是,它的参数是返回条目的键类型。

我尝试如下:

<K, V, RE extends E & Entry<K,V>> V get(K k);

不幸的是,一个类型参数用不同的类型参数实现一个接口两次是非法的,或者如 javac 所说:

类型变量后面不能跟 其他界限

【讨论】:

    【解决方案3】:

    将参数化移动到您的类本身?

    public class X<A, R...> {
        private Map<A,R> blah;
        public R method(A type) {}
    }
    

    【讨论】:

    • 对类进行参数化意味着地图中的所有元素都必须属于同一类型。这不是我想要的。
    • 啊,抱歉,很难弄清楚您要做什么。我想很难表达:-p
    猜你喜欢
    • 2018-04-04
    • 1970-01-01
    • 2013-05-06
    • 1970-01-01
    • 1970-01-01
    • 2021-07-05
    • 1970-01-01
    • 2012-07-14
    相关资源
    最近更新 更多