如何将通配符(?)转换为泛型类型T?

5

我有一个静态方法,想在运行时使用它来加载类并实例化我的对象,但是当我编译时,出现了这个警告:

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

以下是代码示例:
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());
    }
}

有人能给我解释一下吗?

[更新] 这是一个方法调用的示例:

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);
2个回答

3

使用字符串表示类,不能以安全的方式完成该操作。首先,编译器无法知道字符串集合是否实际包含了完全限定的类名;即使这些是类名,也不能保证名称指定的类是T的子类。

应该传入一组受限于T子类的类集合,而不是传入字符串集合。

Set<Class<? extends T>> classes

然后,您可以遍历这些类,并且无需进行任何转换:
for (Class<? extends T> clazz : classes) {
  Constructor<? extends T> ctor = clazz.getConstructor();
  T t = ctor.newInstance();
  list.add(t);
}

如果推迟类的初始化是必须的,那么你只能在这个方法中适当地添加@SuppressWarnings,并希望加载这些字符串的配置是正确的。


我使用Set<String>,因为类名是从文件中读取的。 我将要使用的类保存在文件中。 - ehbarbian
@ehbarbian请参考最后一句话。从类型的角度来看,您正在做一些本质上不安全的事情。 - Andy Turner

1

有一点需要补充。首先,您尝试将每个对象强制转换为T。如果您预先知道T,我不认为您需要传递一组字符串,而类对象就可以做到。

假设如果还需要在可能存在子类的情况下:

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

那就是我想要的。加载子类(请参见更新)。我猜你的代码可以帮忙。 - ehbarbian
3
您可以选择在类之前更改类型 Class<? extends T> clazz = Class.forName(...).asSubclass(clazz); - newacct

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接