从String变量(Java)实例化泛型类

时间:2016-06-01 13:55:20

标签: java string class generics

我试图在Java中实例化一个给定String的类。像这样:

// Pseudocode
String name = "MyClass";
SomeClass<name> sc = new SomeClass<name>(parameter);

在我的例子中,构造函数采用Class<E>类型。所以我会通过MyClass.class。理想情况下,取字符串并附加.class。结果会是这样的:

SomeClass<name> sc = new SomeClass<name>(name.class);

我在这里看到答案:Create new class from a Variable in Java但无法让它发挥作用。使用该链接中的信息,我尝试了这个:

String name = "MyClass";
Class className = Class.forName(name);
className param1;
Constructor con = className.getConstructor(className.class);
SomeClass<className> sc = con.newInstance(param1);

当然,这不起作用。我将如何实现这一目标?

3 个答案:

答案 0 :(得分:3)

泛型主要是一个编译时工具,可帮助编译器发现错误。由于类型擦除,大多数通用信息在运行时丢失 - 除了反射数据中的一些基本信息(这对您没有真正的帮助)。

让我们假设您的构造函数如下所示:

public SomeClass( Class<T> param ) { ... }

由于运行时类型擦除,它看起来像这样(在被调用时):

public SomeClass( Class param ) { ... }

正如您所看到的,没有通用信息,因此要使用SomeClass.getConstructor( Class.class )

然后您可以使用newInstance( Class.forName("java.lang.String") )调用它。 您再次看到您基本上能够将任何类作为参数传递,这是由于泛型是编译时工具,即编译器应用这些检查运行时信任编译的代码。

如果你需要使用任何边界,你需要使用不同的方法或检查在构造函数中传递的正确类(例如,使用方法Class#isAssignableFrom())。

将创建的实例分配给变量时,您需要使用通配符,即SomeClass<?> sc或原始类型,即SomeClass sc(我建议使用通配符)。当然编译器不允许你调用任何只存在于某些类中的方法(例如在MyClass中),因为它根本不知道(并且它不能只知道类的定义)名称)。

修改

为了能够澄清最后一部分,我将尝试添加另一个例子。

假设MyClass有方法foo()SomeClass有方法T getTypeInstance()。如果您的变量类型为SomeClass<MyClass> sc,则可以进行调用sc.getTypeInstance().foo(),因为编译器知道T已绑定到MyClass

然而,当使用反射信息丢失时,即因为您必须使用SomeClass<?> sc,编译器将不知道getTypeInstance()的返回类型,因此不允许调用{{ 1}}。

如果您知道您可以作为参数传递的所有类实现相同的接口,那么您可以执行类似foo()的编译,因为SomeClass<CommonInterface> sc将返回原始类型,因此编译器检查在此处被禁用。在这种情况下,您可以将Constructor#newInstance()添加到foo()并能够调用它 - 至少除非您破坏代码。

上面的问题是你也可以将CommonInterface传递给构造函数,编译器和运行时允许但由于String.class没有实现String,这仍然是错误的。 。因此,在尝试创建实例,将其强制转换为CommonInterface或在其上调用CommonInterface时,您将获得异常 - 除非您在构造函数中添加一个拒绝任何不属于类的参数的检查可分配给foo()(例如,通过检查CommonInterface)。

答案 1 :(得分:1)

假设您的泛型类看起来像这样:

public GenericClass(Class<T> param) {
    System.out.println(param.getName());
}

您的代码可能如下所示:

@SuppressWarnings("unchecked")
public static void main(String[] args) throws Exception {
    String type = "java.lang.String";
    Class g = GenericClass.class;
    Constructor c = g.getConstructor(Class.class);
    GenericClass<?> genericClass = (GenericClass<?>) c.newInstance(Class.forName(type));
}

你会在控制台中看到它打印&#34; java.lang.String&#34;。

这是有效的,因为在反射中,所有内容都必须具有类型,并且由于泛型在编译中没有类型,因此它显示为java.lang.Class,而不是java.lang.Class<T>。因此,要获取构造函数,必须告诉它它找到构造函数获取类对象。然后,只需使用Class.forNametype中查找类,然后调用构造函数,传入该类。

答案 2 :(得分:0)

我在您的代码中看到了一些错误:

什么是:className param1;?您不能将作为类className的对象的Class用作类型。

邀请:

Constructor con = className.getConstructor(className.class);

返回类的构造函数&#34; className&#34;参数类型为&#34; className&#34;。这是你的要求吗? (例如,如果您的类为ArrayList,则构造函数采用类型ArrayList的1个参数)

代码:

SomeClass<className> sc = con.newInstance(param1);

语法不正确。方法newInstance返回Object类型T。尝试将其分配给Object类型的变量。

以下是您的代码应该如何显示:

String name = "MyClass";
Class className = Class.forName(name);
String param1 = "something";
// Using a constructor that use a String as parameter
Constructor con = className.getConstructor(String.class); 
Object sc = con.newInstance(param1);