如何声明具有多个具有非平凡关系的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 super A>
,但是如果你假设你只是从它那里获得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) {} }