简体   繁体   English

泛型和Class.forName

[英]Generics and Class.forName

I would like to create an instance of a specified class using its name. 我想使用其名称创建指定类的实例。 My code is shown below. 我的代码如下所示。

I get a compiler warning. 我收到编译器警告。 Am I doing this the right way? 我这样做是对的吗? Is it even possible to use the name of a class and get an instance of that type back, as I don't think there is any way of the compiler knowing what the type should be? 甚至可以使用类的名称并获取该类型的实例,因为我认为编译器不知道该类型应该是什么方式?

public static <T> T create(final String className) {
    try {
        final Class<?> clazz = Class.forName(className);

        //WARNING: Type safety: Unchecked cast from capture#2-of ? to T
        return (T) create(clazz); 
    }
    catch (ClassNotFoundException e) {
        e.printStackTrace();
    }
}

public static <T> T create(final Class<T> classToCreate) {
    final Constructor<T> constructor;
    try {
        constructor = classToCreate.getDeclaredConstructor();
        final T result = constructor.newInstance();
        return result;
    }
    catch (Exception e) {
        e.printStackTrace();
    }
}

Thanks 谢谢

I think that the first method should look something like this: 我认为第一种方法应该是这样的:

public static <T> T create(final String className, Class<T> ifaceClass) 
throws ClassNotFoundException {
    final Class<T> clazz = Class.forName(className).asSubclass(ifaceClass);
    return create(clazz); 
}

You cannot do an up-cast typecast using a type parameter ... without those pesky type-safety warnings. 你不能使用类型参数进行上传类型转换...没有那些讨厌的类型安全警告。

By the way, if you ignore those warnings, the create method may create an instance of some class that isn't compatible with the actual type used by the caller. 顺便说一句,如果忽略这些警告,create方法可能会创建某个类的实例,该实例与调用者使用的实际类型不兼容。 This is likely to lead to an unexpected ClassCastException later on; 这可能会在以后导致意外的ClassCastException; eg when the instance is assigned. 例如,在分配实例时。


EDIT: @Pascal points out that we need to add a typecast to make this compile; 编辑:@Pascal指出我们需要添加一个类型转换来进行编译; ie

Class<T> clazz = (Class<T>) Class.forName(className).asSubclass(ifaceClass);

Unfortunately, we also need to add a @SuppressWarnings annotation. 不幸的是,我们需要添加@SuppressWarnings注释。

I think this is because Class.forName(..) isn't parameterized for T. When you trigger the eclipse autocomplete, it assumes the clazz.newInstance() return Object. 我认为这是因为Class.forName(..)没有为T参数化。当你触发eclipse自动完成时,它假定clazz.newInstance()返回Object。 So, retain the cast and add @SuppressWarnings. 因此,保留演员表并添加@SuppressWarnings。 If you don't use the method properly (ie String str = Utils.create("java.util.ArrayList"); ) then a ClassCastException will occur, but that is normal. 如果您没有正确使用该方法(即String str = Utils.create("java.util.ArrayList"); )那么将发生ClassCastException ,但这是正常的。

The second method is fine. 第二种方法很好。


But for the first one, any class name could be passed as a parameter. 但是对于第一个,任何类名都可以作为参数传递。

The point of the method would be to instanciate a class that the calling code doesn't know at compile-time, it only knows about it at runtime. 该方法的要点是实现一个调用代码在编译时不知道的类,它只在运行时知道它。

In these conditions, the calling code cannot set the Type Parameter, so the method cannot be parameterized, so the cast has no point... 在这些条件下,调用代码不能设置类型参数,因此方法不能参数化,所以演员没有意义......

Therefore, I would say the whole method has no point . 因此,我会说整个方法没有意义


Some point could be given to the method if the calling code would know a supertype of the class . 如果调用代码知道类的超类型,则可以给出该方法的一些观点。 That could be passed as a parameter, and the cast would be possible and meaningful. 这可以作为参数传递,并且演员阵容将是可能且有意义的。

public static <T> T create(final Class<T> superType, final String className) {
  try {
    final Class<?> clazz = Class.forName(className);
    final Object object = clazz.newInstance();
    if (superType.isInstance(object)) {
      return (T)object; // safe cast
    }
    return null; // or other error 
  } // catch and other error code
}

You can not restrict a type parameter to contain the type named className. 您不能将类型参数限制为包含名为className的类型。 Hence, a caller can supply any type to your create(String) function, which is of course not type safe. 因此,调用者可以为您的create(String)函数提供任何类型,这当然不是类型安全的。

Since you cannot statically enforce that the returned instance implements any interface (without having the caller tell you by passing the corresponding Class object), I'd dispense with generics, and have the caller cast to whatever type he expects. 由于您无法静态强制执行返回的实例实现任何接口(没有调用者通过传递相应的Class对象告诉您),我将免除泛型,并将调用者强制转换为他期望的任何类型。

public static Object create(final String className) {
    try {
        final Class<?> clazz = Class.forName(className);
        return create(clazz); 
    }
    catch (ClassNotFoundException e) {
        e.printStackTrace();
    }
}

A caller can then write: 然后调用者可以写:

Foo foo = (Foo) create("my.cool.FooBar");

as opposed to 而不是

Foo foo = create("my.cool.FooBar", Foo.class);

Even if you can get the code above to work, the newInstance() method of constructor assumes a zero argument constructor. 即使您可以使上面的代码工作,构造函数的newInstance()方法也假定零参数构造函数。

If the Class does not have one ie the zero argument constructor of the Class you are trying to create has been explicitly declared private, or package depending on where the method is called from, you will get an IllegalAccessException. 如果类没有一个,即您尝试创建的类的零参数构造函数已显式声明为private,或者根据调用方法的位置而打包,则会出现IllegalAccessException。 Something to add to your precondition, if you get it working. 如果你让它发挥作用,可以添加到你的前提条件中。

I found an interesting thing: Type can be converted to Class directly if it is not a generic type. 我发现了一件有趣的事情:如果Type不是泛型Type可以直接转换为Class But i still can not Class.forName("java.util.List<SomeType>") . 但我仍然不能Class.forName("java.util.List<SomeType>")

import java.lang.reflect.*;
import java.util.*;

public class Main {
    public void func(List<List<String>> listlist, Map<String, List<String>> list, String s) {}

    public static void main(String[] args) throws ClassNotFoundException {
        Method m = Main.class.getDeclaredMethods()[1];

        Type t0 = m.getGenericParameterTypes()[0];
        boolean b0 = t0 instanceof Class;

        boolean b01 = ((ParameterizedType)t0).getRawType() instanceof Class;

        Type t1 = m.getGenericParameterTypes()[2];
        boolean b1 = t1 instanceof Class;
    }
}

在此输入图像描述

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

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