[英]How to use lambdaJ's extract() on method returning generic type without explicit casting
我正在使用泛型来存储对任意对象的引用。
class Option<TypeT>{
TypeT o;
Option(TypeT t){
this.o = o;
}
TypeT getReferencedObject(){
return o;
}
}
我想使用lambdaJ从集合中提取这些对象。 现在我正在使用非常难看的双重(List<TypeT>)(List<?>)
,我正在寻找更好/更清洁的解决方案。 问题出在on( sth ).getReferencedObject()
语句中,该语句不能像on(Option<String>.class).getReferencedObject()
一样参数化on(Option<String>.class).getReferencedObject()
List<Option<String>> options = Arrays.asList(new Option<String>("AAA"), new Option<String>("BBB"));
List<String> string = (List<String>)(List<?>) extract(options, on(Option.class).getReferencedObject());
这个问题源于语言本身的限制 - 对于具体的参数化类型没有类文字 ,这是Java使用类型擦除来实现泛型的结果。
具体参数化类型的示例是Option<String>
。 没有Option<String>.class
,只有Option.class
。
参数化类型可以在运行时由ParameterizedType
实例表示,可以使用反射获取。 一些库利用这一点来安全地表示泛型类型,例如Guava的TypeToken<T>
。
但遗憾的是,这并没有帮助。 原因是LambdaJ的on
方法使用动态代理来表示指定的类型。 这样就可以调用这样的东西:
on(Option.class).getReferencedObject()
可以在幕后工作它的LambdaJ魔法。 创建这样的代理,最终归结为对Java API的调用Proxy.newProxyInstance
,或者自定义代理实现像net.sf.cglib.proxy.Enhancer
。 在任何一种情况下, Class
实例都专门用于表示代理接口/类。 所以总结起来, on
不能接受任何东西,但一个冷酷的Class<T>
你能做什么? 你的选择有限......
我们知道这是丑陋的,它肯定容易出错,但它有效:
@SuppressWarnings("unchecked") //okay because options is a List<Option<String>>
List<String> strings =
(List<String>)(List<?>)extract(options, on(Option.class).getReferencedObject());
出于好奇,我尝试了这个:
@SuppressWarnings("unchecked") //not really okay, but let's see what happens
Class<Option<String>> onWhat = (Class<Option<String>>)(Class<?>)Option.class;
List<String> strings = extract(options, on(onWhat).getReferencedObject());
但这让我有一个运行时异常:
java.lang.ClassCastException
:net.sf.cglib.empty.Object$$EnhancerByCGLIB$$9d9c54e1
无法net.sf.cglib.empty.Object$$EnhancerByCGLIB$$9d9c54e1
为java.lang.String
List<String> strings = Lists.transform(
options,
new Function<Option<String>, String>() {
@Override
public String apply(Option<String> option) {
return option.getReferencedObject();
}
}
);
这看起来相当嘈杂,但Function
可以至少抽象出来:
class Option<T> {
...
public static final class Functions {
//Impl note: Effective Java item 27
private static final Function<?, ?> GET_REFERENCED_OBJECT =
new Function<Option<?>, Object>() {
@Override
public Object apply(Option<?> option) {
return option.getReferencedObject();
}
};
private Functions() { }
public static <T> Function<Option<T>, T> getReferencedObject() {
//okay for any T, since Option<T>.getReferencedObject must return a T
@SuppressWarnings("unchecked")
final Function<Option<T>, T> withNarrowedTypes =
(Function<Option<T>, T>)GET_REFERENCED_OBJECT;
return withNarrowedTypes;
}
}
}
这使得呼叫站点更清洁:
List<String> strings = Lists.transform(
options,
Option.Functions.<String>getReferencedObject()
);
请注意, Lists.transform
返回原始List
的转换视图 。 如果你想要一个像LambdaJ的extract
返回的副本,那么制作一个很容易:
List<String> strings = Lists.newArrayList(Lists.transform(
options,
Option.Functions.<String>getReferencedObject()
));
这更加冗长,但它为您提供了通用的类型安全性,您可以在幕后删除动态代理巫术。
List<String> strings = new ArrayList<>(options.size());
for (Option<String> option : options) {
strings.add(option.getReferencedObject());
}
一个经常被忽视但令人愉快的选择!
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.