簡體   English   中英

如何在沒有顯式轉換的情況下返回泛​​型類型的方法上使用lambdaJ的extract()

[英]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>

你能做什么? 你的選擇有限......

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.ClassCastExceptionnet.sf.cglib.empty.Object$$EnhancerByCGLIB$$9d9c54e1無法net.sf.cglib.empty.Object$$EnhancerByCGLIB$$9d9c54e1java.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());
}

一個經常被忽視但令人愉快的選擇!

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM