Win*_*ter 6 java generics dictionary type-safety java-8
我想创建一个IdentityHashMap<Class<T>, Consumer<T>>.基本上,我想用一种方法来映射一个类型,说明如何处理这种类型.
我想动态地能够用对象X说,执行Y.我能做到
private IdentityHashMap<Class<?>, Consumer<?>> interceptor = new IdentityHashMap<>();
Run Code Online (Sandbox Code Playgroud)
但它很糟糕,因为我在使用它时必须在lamba中投射对象.
例:
interceptor.put(Train.class, train -> {
System.out.println(((Train)train).getSpeed());
});
Run Code Online (Sandbox Code Playgroud)
我想做的是
private <T> IdentityHashMap<Class<T>, Consumer<T>> interceptor = new IdentityHashMap<>();
Run Code Online (Sandbox Code Playgroud)
但似乎不允许这样做.有没有办法做到这一点 ?使用此类型的方法映射类型的最佳解决方法是什么?
这基本上就像Joshua Bloch描述的类型安全的异构容器,除了你不能使用它Class来转换结果.
奇怪的是,我找不到SO上存在的一个很好的例子,所以这里有一个:
package mcve;
import java.util.*;
import java.util.function.*;
class ClassToConsumerMap {
private final Map<Class<?>, Consumer<?>> map =
new HashMap<>();
@SuppressWarnings("unchecked")
public <T> Consumer<? super T> put(Class<T> key, Consumer<? super T> c) {
return (Consumer<? super T>) map.put(key, c);
}
@SuppressWarnings("unchecked")
public <T> Consumer<? super T> get(Class<T> key) {
return (Consumer<? super T>) map.get(key);
}
}
Run Code Online (Sandbox Code Playgroud)
这是类型安全的,因为键和值之间的关系是由put方法的签名强制执行的.
关于Java泛型的局限性的一个令人讨厌的事情是,这些容器中的一个不能为通用值类型编写,因为没有办法做到,例如:
class ClassToGenericValueMap<V> {
...
public <T> V<T> put(Class<T> key, V<T> val) {...}
public <T> V<T> get(Class<T> key) {...}
}
Run Code Online (Sandbox Code Playgroud)
其他说明:
我会使用常规HashMap或LinkedHashMap为此.HashMap更好的维护和许多优化,IdentityHashMap没有.
如果有必要使用泛型类型,Consumer<List<String>>那么你需要使用像Guava TypeToken这样的键作为键,因为Class它只能表示类型的擦除.
ClassToInstanceMap当你需要时,番石榴有一个Map<Class<T>, T>.
有时人们希望通过类到消费者的地图做这样的事情:
public <T> void accept(T obj) {
Consumer<? super T> c = get(obj.getClass());
if (c != null)
c.accept(obj);
}
Run Code Online (Sandbox Code Playgroud)
也就是说,给定任何对象,找到绑定到该对象类的映射中的使用者,并将该对象传递给使用者的accept方法.
这个例子就不能编译,但是,因为getClass()实际上被指定为返回Class<? extends |T|>,这里|T|是指删除的T.(参见JLS§4.3.2).在上面的例子中,擦除T是Object,那么obj.getClass()返回一个普通的Class<?>.
使用捕获帮助器方法可以解决此问题:
public void accept(Object obj) {
accept(obj.getClass(), obj);
}
private <T> void accept(Class<T> key, Object obj) {
Consumer<? super T> c = get(key);
if (c != null)
c.accept(key.cast(obj));
}
Run Code Online (Sandbox Code Playgroud)
此外,如果您希望其修改版本get返回任何适用的消费者,您可以使用以下内容:
public <T> Consumer<? super T> findApplicable(Class<T> key) {
Consumer<? super T> c = get(key);
if (c == null) {
for (Map.Entry<Class<?>, Consumer<?>> e : map.entrySet()) {
if (e.getKey().isAssignableFrom(key)) {
@SuppressWarnings("unchecked")
Consumer<? super T> value =
(Consumer<? super T>) e.getValue();
c = value;
break;
}
}
}
return c;
}
Run Code Online (Sandbox Code Playgroud)
这让我们可以将普通超类型消费者放在地图中,如下所示:
ctcm.put(Object.class, System.out::println);
Run Code Online (Sandbox Code Playgroud)
然后使用子类类检索:
Consumer<? super String> c = ctcm.findApplicable(String.class);
c.accept("hello world");
Run Code Online (Sandbox Code Playgroud)
这是一个稍微更一般的例子,这次使用UnaryOperator并且没有有界通配符:
package mcve;
import java.util.*;
import java.util.function.*;
public class ClassToUnaryOpMap {
private final Map<Class<?>, UnaryOperator<?>> map =
new HashMap<>();
@SuppressWarnings("unchecked")
public <T> UnaryOperator<T> put(Class<T> key, UnaryOperator<T> op) {
return (UnaryOperator<T>) map.put(key, op);
}
@SuppressWarnings("unchecked")
public <T> UnaryOperator<T> get(Class<T> key) {
return (UnaryOperator<T>) map.get(key);
}
}
Run Code Online (Sandbox Code Playgroud)
第? super一个示例中的有界通配符特定于使用者,我认为没有通配符的示例可能更容易阅读.
这是可以实现这一个类型安全的方式没有任何选中投.解决方案在于将更换Consumer<T>为更一般的Consumer<Object>转换,然后委托给原始使用者:
public class ClassToConsumerMap {
private final Map<Class<?>, Consumer<Object>> map = new IdentityHashMap<>();
public <T> Consumer<? super T> put(Class<T> key, Consumer<? super T> c) {
return map.put(key, o -> c.accept(key.cast(o)));
}
public <T> Consumer<? super T> get(Class<T> key) {
return map.get(key);
}
}
Run Code Online (Sandbox Code Playgroud)
根据您的需要,get()也可以简单地返回一个Consumer<Object>.如果您只是在运行时知道类型,那么这是必要的,例如
classToConsumerMap.get(someObject.getClass()).accept(someObject);
Run Code Online (Sandbox Code Playgroud)
我很确定我在2016年@ Devoxx比利时的谈话中看到了这个解决方案(或类似的东西),可能来自Venkat Subramaniam,但我明确地找不到它...
| 归档时间: |
|
| 查看次数: |
3148 次 |
| 最近记录: |