简体   繁体   中英

How to cast Wildcard (?) to a Generic type T?

I have a static method that i want to use to load classes and instantiate my objects at runtime, but when i compile, i got this warning:

warning: [unchecked] unchecked cast
            T t = (T) ctor.newInstance();
required: T
found:    CAP#1
where T is a type-variable:
    T extends Object declared in method <T>forName(String,Set<String>)
    where CAP#1 is a fresh type-variable:
    CAP#1 extends Object from capture of ?
1 warning

Here's the code:

public static <T> Set<T> forName(String modulePath, Set<String> classes) throws InvalidModuleException{
    try {
        ClassLoader cl = new URLClassLoader(new URL[]{new URL(modulePath)});

        Set<T> list = new HashSet<>(classes.size());
        for (String className : classes) {
            Class<?> clazz = (Class<?>) Class.forName(className, true, cl);
            Constructor<?> ctor = clazz.getConstructor();
            T t = (T) ctor.newInstance();
            list.add(t);
        }
        return list;    
    } catch (MalformedURLException | ReflectiveOperationException ex) {
        throw new InvalidModuleException(ex.getMessage());
    }
}

Someone can explain me that?

[UPDATE] Here's and example of method call:

HashSet<String> set = new HashSet<>();
h.add("fully_classfied_classname_readed_from_file"); //Class that extends AbstractApplication
Set<AbstractApplication> abs = Apps.forName("plugins/module.jar", set);

You cannot do that in a safe way using strings to indicate the classes. For one thing, there is no way for the compiler to know that the set of strings actually contains fully-qualified class names; even if they are class names, there is no guarantee that the classes specified by the names are subclasses of T .

Instead of passing in a set of strings, pass in a set of classes, constrained to be subclasses of T :

Set<Class<? extends T>> classes

Then you can just iterate over these classes, and remove the need for any casting:

for (Class<? extends T> clazz : classes) {
  Constructor<? extends T> ctor = clazz.getConstructor();
  T t = ctor.newInstance();
  list.add(t);
}

If the requirement to defer initializations of the classes is absolute, you have little choice but to add @SuppressWarnings appropriately to this method, and hope that the configuration from which these strings are loaded is correct.

There is one missing bit. First, you try to typecast every object to T . If you know T prehand, I do not see a reason on who you need to pass a set of strings when a class object would do

Suppose if you still need to in case of possibility of sub-classes:

public static <T> Set<? extends T> forName(String modulePath, Set<String> classes, Class<T> claz) throws InvalidModuleException{
    try {
    ClassLoader cl = new URLClassLoader(new URL[]{new URL(modulePath)});

    Set<T> list = new HashSet<>(classes.size());
    for (String className : classes) {
        Class<?> clazz = Class.forName(className, true, cl);
        Constructor<?> ctor = clazz.getConstructor();
        Object obj = ctor.newInstance();
        list.add(claz.cast(obj));
    }
    return list;
} catch (MalformedURLException | ReflectiveOperationException | ClassCastException ex) {
    throw new InvalidModuleException(ex.getMessage());
}
}

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