如何在没有显式强制转换的情况下在返回泛型类型的方法上使用lambdaJ的extract()



我使用泛型来存储对任意对象的引用。

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() 那样参数化

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>

你能做什么?您的选择有限。。。

1( 坚持你所拥有的:

我们知道这很难看,而且肯定很容易出错,但它很有效:

@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无法转换为java.lang.String

2( 使用番石榴:

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()
));

这一切都要详细得多,但它为您提供了通用的类型安全性,您可以将动态代理魔法抛在幕后。

3( 使用Ol‘Plain n’Simple(然后等待Java 8(:

List<String> strings = new ArrayList<>(options.size());
for (Option<String> option : options) {
    strings.add(option.getReferencedObject());
}

一个经常被忽视但令人愉快的可行选择!

相关内容

  • 没有找到相关文章

最新更新