以下是我想在我的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;
但它没有用,我还需要演员。
答案 0 :(得分:4)
没有干净的方法可以做到这一点。您可以做的最好的就是您已经完成的工作 - 隐藏强制类型安全的Map
隐藏方法,并在这些方法中隐藏必要的强制转换。
从 Effective Java 第2版,第29项:“考虑类型安全的异构容器”(其中Josh Bloch勾勒出你正在做的更简单的版本,一个“地图”,其中的键是类和值是键类的实例):
接下来要注意的是
favorites
地图的值类型只是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<R, A>
,这实际上并不正确 - 它应该是? super A
。(我们不知道它是什么除了A
之外,它可以处理 - 它可以是A
的任何超类,一直到Object
。)在这种特殊情况下,它可能足够安全,但是如果你考虑类似于List
,我们可能遇到很多麻烦:您可以安全地将A
放入List<? super A>
,但如果您认为自己只会获得A
s在它之外,你将获得ClassCastExceptions
。)
答案 1 :(得分:1)
因此,您希望在不进行强制转换的情况下实现类型安全的异构容器。
有趣的谜题。
我认为这在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<MyEntry<?>>
的呼叫者现在可以确保条目是类型安全的,并且Map中指定的put
方法可以很好地强制执行此操作。此外,get
方法保证返回正确的映射。我无法表达的是,它的参数是返回条目的密钥类型。
我尝试了如下:
<K, V, RE extends E & Entry<K,V>> V get(K k);
不幸的是,类型参数使用不同的类型参数实现两次接口是非法的,或者javac将它放入:
可能不会跟随类型变量 其他界限
答案 2 :(得分:0)
将参数化移动到类本身?
public class X<A, R...> {
private Map<A,R> blah;
public R method(A type) {}
}