【问题标题】:How to cast Wildcard (?) to a Generic type T?如何将通配符(?)转换为通用类型 T?
【发布时间】:2016-01-13 12:37:41
【问题描述】:

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

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

【问题讨论】:

    标签: java generics casting wildcard


    【解决方案1】:

    您不能以安全的方式使用字符串来指示类。一方面,编译器无法知道字符串集实际上包含完全限定的类名;即使是类名,也不能保证名称指定的类是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 因为类名是从文件中读取的。我将我将使用的类保存在一个文件中。
    • @ehbarbian 请参考最后一句话。从类型的角度来看,您正在做一些本质上不安全的事情。
    【解决方案2】:

    少了一位。首先,您尝试将每个对象类型转换为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());
    }
    }
    

    【讨论】:

    • 这就是我想要的。加载子类(参见更新)。我想你的代码可以提供帮助。
    • 或者,您可以预先更改类上的类型Class&lt;? extends T&gt; clazz = Class.forName(...).asSubclass(claz);
    猜你喜欢
    • 2017-01-22
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2013-05-02
    • 1970-01-01
    相关资源
    最近更新 更多