如何声明具有多个具有非平凡关系的generics类型的成员?

以下是我想在我的java代码中编写的内容:

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

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

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

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

更多细节

这是我想要做的精确版本,虽然它更加危险:

我希望我能做到:

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

相反,我必须这样做:

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

我的方法强制执行所需的关系,看起来像这样:

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

我想要以下方法:

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

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

我试过的事情

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

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

但它没有用,我还需要演员。

没有干净的方法来做到这一点。 你能做的最好的就是你已经完成的事情 – 隐藏强制类型安全的Map方法,并在这些方法中隐藏必要的强制转换。

Effective Java第2版​​,第29项:“考虑类型安全的异构容器”(其中Josh Bloch勾勒出你正在做的更简单的版本,一个“map”,其中键是类,值是键类的实例) ):

接下来要注意的是, favorites Map的值类型只是Object 。 换句话说, Map不保证键和值之间的类型关系。 事实上,Java的类型系统不足以表达这一点。 但我们知道这是真的,当我们找到喜欢的时候,我们会利用它。

鉴于您无法为值获取任何有用的类型强制执行,最简单的实现可能是这样的:

 public class HandlerRegistry { private Map, Object> map = new HashMap, Object>(); public > void addHandler(Class actionClass, ActionHandler handler) { map.put(actionClass, handler); } @SuppressWarnings("unchecked") public > ActionHandler findHandler(A action) { return (ActionHandler) map.get(action.getClass()); } } 

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

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

注意:最初上面的第二个例子返回了ActionHandler ,它实际上并不正确 – 它应该是? super A (除了A之外我们不知道它能处理什么 – 它可以是任何超类A ,一直到Object 。)在这种特殊情况下,它可能足够安全,但是如果你考虑类似List东西,我们可能会遇到很多麻烦:你可以安全地将A放入List ,但是如果你假设你只是从它那里获得A ,那么你将获得ClassCastExceptions 。)

因此,您希望在不进行强制转换的情况下实现类型安全的异构容器

有趣的谜题。

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

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

持有Map>的调用者现在可以确保条目是类型安全的,并且Map中指定的put方法可以很好地强制执行此操作。 此外, get方法保证返回正确的映射。 我无法表达的是,它的参数是返回条目的密钥类型。

我试过这个如下:

 > V get(K k); 

不幸的是,类型参数使用不同的类型参数实现两次接口是非法的,或者javac将它放入:

类型变量可能不会跟随其他边界

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

 public class X { private Map blah; public R method(A type) {} }