簡體   English   中英

使用類實例作為泛型類型

[英]Use class instance as generic type

有沒有辦法使用SomeClass.class作為類型的泛型類型?

例如,我有一個枚舉:

enum MyEnum {
    FOO(String.class);
    private Class fooClass;
    private MyEnum(Class fooClass) {
        this.fooClass = fooClass;
    }
    public Class getFooClass(){
        return fooClass;
    }
}

然后在我的代碼中的某個地方:

List<MyEnum.FOO.getFooClass()> list = new ArrayList<>();

在編譯時評估泛型,而此調用的值為:

MyEnum.FOO.getFooClass()

只在運行時才知道。

通用類型在編譯時使用,而不是在運行時使用 - 看看Java如何使用泛型擦除。 結果,你不能這樣做。

你可能能夠做到,這是更詳細的不少,就是用鑄造方法你從枚舉有,而你遍歷列表中的類,但即使如此,你將無法做任何動態通用分配。

其實,這可能的一個黑客位,這就是為什么GSON是能夠構建的List<T>從JSON -它采用了TypeToken<T>類來解決“輸入通用父”匿名typetoken實施。

基於此,我認為應該可以從Class<T>對象獲取List<T>實例,如果我們可以以某種方式提取JSON反序列化,並且只是像這樣構造列表本身。

//from https://stackoverflow.com/a/18321048/2413303
private <T> List<T> getList(Class<T> elementType) {
    ...
    TypeToken<List<T>> token = new TypeToken<List<T>>() {}
        .where(new TypeParameter<T>() {}, elementType);
    List<T> something = gson.fromJson(data, token); //this would have needed to be replaced
    ...
}

我試圖找出GSON為獲得超類所做的工作。

所以我發現了這個

//from https://stackoverflow.com/a/75345/2413303
//from subclass
T instance = ((Class)((ParameterizedType)this.getClass().
       getGenericSuperclass()).getActualTypeArguments()[0]).newInstance();

然后我嘗試了這個:

public static abstract class MyTypeToken<T> {
    public T factory() {
        try {
            Type type = getClass().getGenericSuperclass();
            ParameterizedType paramType = (ParameterizedType) type;
            return ((Class<T>) paramType.getActualTypeArguments()[0]).newInstance();
        } catch (InstantiationException e) {
            e.printStackTrace();
            throw new RuntimeException(e);
        } catch (IllegalAccessException e) {
            e.printStackTrace();
            throw new RuntimeException(e);
        }
    }
}

    MyTypeToken<String> typeToken = new MyTypeToken<String>() {
    };
    System.out.println(typeToken.factory().getClass().getName()); //works and prints java.lang.String

但是以下崩潰:

public <T> List<T> getList(Class<T> clazz) {
    MyTypeToken<ArrayList<T>> token = new MyTypeToken<ArrayList<T>>() {};
    return token.factory();
}

Exception in thread "main" java.lang.ClassCastException: sun.reflect.generics.reflectiveObjects.ParameterizedTypeImpl cannot be cast to java.lang.Class

所以我很確定Google會遇到這種情況,正如他們在TypeToken中的魔術代碼以及更重要的$ Gson $ Types中的Canonicalize方法所表明的那樣,他們使用內部Sun類的重新實現版本,例如ParametrizedTypeImpl等(因為他們有私人訪問權限 )。

所以,如果你像這樣制作一個“enum”:

public static abstract class MyEnum<T> {
    public static final MyEnum<String> FOO = new MyEnum<String>(new MyTypeToken<String>() {}) {};

    protected MyTypeToken<T> typeToken;

    private MyEnum(MyTypeToken<T> typeToken) {
        this.typeToken = typeToken;
    }

    public MyTypeToken<T> getTypeToken() {
        return typeToken;
    }
}

public void execute() {
    String string = MyEnum.FOO.getTypeToken().factory();
    System.out.println(string.getClass().getName());
}

然后你得到java.lang.String ,但是如果你將它與ArrayList<String> ,那么你得到了

Exception in thread "main" java.lang.ClassCastException: sun.reflect.generics.reflectiveObjects.ParameterizedTypeImpl cannot be cast to java.lang.Class
    at com.company.Main$TypeToken.factory(Main.java:19)
    at com.company.Main.execute(Main.java:49)
    at com.company.Main.main(Main.java:55)
    at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
    at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
    at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
    at java.lang.reflect.Method.invoke(Method.java:483)
    at com.intellij.rt.execution.application.AppMain.main(AppMain.java:134)

所以我想 如果你不想竊取GSON內部代碼就 可以 做到這一點(使用GSON和它自己的TypeToken):

import com.google.gson.Gson;
import com.google.gson.reflect.TypeToken;
import java.util.ArrayList;
import java.util.List;

/**
 * Created by EpicPandaForce on 2015.05.06..
 */
public class Main {
    public static abstract class MyEnum<T> { //typed `enum`-like structure
        public static final MyEnum<String> FOO = new MyEnum<String>(new TypeToken<String>() {}) {};

        protected TypeToken<T> typeToken;

        private MyEnum(TypeToken<T> typeToken) {
            this.typeToken = typeToken;
        }

        public TypeToken<T> getTypeToken() {
            return typeToken;
        }
    }

    public static <T> TypeToken<ArrayList<T>> getListToken(TypeToken<T> typeToken) {
        return new TypeToken<ArrayList<T>> () {};
    }

    public void execute() {
        Gson gson = new Gson();
        List<String> list = gson.fromJson("[]", getListToken(MyEnum.FOO.getTypeToken()).getType()); //construct empty list using GSON
        list.add("hello");
        System.out.println(list.get(0)); //writes out hello
    }

    public static void main(String[] args) {
        Main main = new Main();
        main.execute();
    }
}

它有效。

最好的是你也可以將“ enum ”的參數替換為Class<T>對象,並獲得相同的結果。 此外,您可以使列表的初始化成為靜態方法的一部分。

這是最終的代碼

public class Main {
    public static abstract class MyEnum<T> {
        public static final MyEnum<String> FOO = new MyEnum<String>(String.class) {};

        protected Class<T> typeToken;

        private MyEnum(Class<T> clazz) {
            this.typeToken = clazz;
        }

        public Class<T> getClazz() {
            return typeToken;
        }
    }

    public static class ListFactory {
        private static Gson gson;

        static {
            gson = new Gson();
        }

        private static <T> TypeToken<ArrayList<T>> getListToken(Class<T> typeToken) {
            return new TypeToken<ArrayList<T>> () {};
        }

        public static <T> List<T> getList(Class<T> clazz) {
            return gson.fromJson("[]", getListToken(clazz).getType());
        }
    }



    public void execute() {
        List<String> list = ListFactory.getList(MyEnum.FOO.getClazz());
        list.add("hello");
        System.out.println(list.get(0));
    }

    public static void main(String[] args) {
        Main main = new Main();
        main.execute();
    }
}

輸出是

hello

但是,是的,你做不了類似的事情

List<MyEnum.FOO.getFooClass()> 

您需要知道您獲得的對象是List<String>

由於明顯的事實,這是不可能的: FOO.getFooClass()表達式的值是runtime評估的,而所有泛型都是compile-time處理的,並且在runtime沒有關於泛型類型的任何信息。

正如其他人已經說過的那樣,你做不到。 在這里查看更多信息: 如何使用反射實例化具有泛型類的java.util.ArrayList

暫無
暫無

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

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