简体   繁体   中英

How can I use a Class object as a parameterized type?

So Google turns up a lot of questions about getting the .class of a parameterized type, but I'm trying to go the other way.

I have a list of Classes, and I need to make a map that uses the Class as a key, and an ArrayList of objects of type Class as the value. Something like this:

Class[] classes = getArrayOfClasses();
HashMap<Class, ArrayList<?>> map = new HashMap<Class, ArrayList<?>>();
for(Class c : classes) {
    map.put(c, new ArrayList<c>());    // here is where the problem is
}

The problem of course is that it needs a parameterized type, not a class. One possible workaround is to just use map.put(c, new ArrayList<Object>()) , but then I have to know the type and cast every object I pull.

MyClass myObj = (MyClass) map.get(MyClass.class).get(0);

I also tried making an initialization function like this:

private <T> ArrayList<T> makeArrayList(Class<T> c) {
    return new ArrayList<T>();
}

This had the syntax that I hoped would work, but it still left me with an ArrayList of Object that had to be cast.

So is there a way I can just make the ArrayList parameterized with the Class's type?

Unfortunately, due to the nature of Java's generics (they work by erasure), generics aren't available at runtime.

This means that there is no real way to make map.put(c, new ArrayList<c>()); work.

I recommend doing this instead: Wrap your list in a dedicated object, and give that object the following accessor method:

public <T> getList(Class<T> key) {
    List<?> list = map.get(key);
    return (List<T>) list;
}

This is going to produce a warning, but as long as the map was constructed properly, you'll be ok.

Alternatively, you can do a run time check of all objects to make sure the type match up.

public <T> getList(Class<T> key) {
    List<?> list = map.get(key);
    for(Object o : list){
        assert(key.isInstance(o));
    }
    return (List<T>) list;
}

There is absolutely no difference in the compiled bytecode between:

new ArrayList();
new ArrayList<T>();
new ArrayList<String>();
new ArrayList<Integer>();

The type parameter has no effect on the compiled code; it only affects the compiler's type-checking. So, if the type parameter is not available at compile-time, it is useless, as compile-time is the only time where it could be useful.

You should just write new ArrayList<Object>() or new ArrayList<Integer>() even new ArrayList<CompletelyBogusUnrelatedClass>() ; it doesn't matter, because all of them are compatible with ArrayList<?> , which is the value type of your map.

As for your method that creates and returns an ArrayList , it should just be written like this:

private static <T> ArrayList<T> makeArrayList() {
    return new ArrayList<T>();
}

(That's right, this method returns an ArrayList of whatever element type you want without even knowing what that type is ! This is a clear demonstration that the type parameter is not needed at runtime.)

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM