Java:创建类型为类型参数的对象
本文关键字:类型参数 对象 类型 创建 Java | 更新日期: 2023-09-27 18:01:25
我想编写与c#代码相当的Java代码。
我的c#代码如下:public abstract class A<T> where T : A<T>, new()
{
public static void Process()
{
Process(new T());
}
public static void Process(T t)
{
// Do Something...
}
}
public class B : A<B>
{
}
public class C : A<C>
{
}
我的代码的Java版本是这样的
public abstract class A<T extends A<T>>
{
public static <T extends A<T>> void process()
{
process(new T()); // Error: Cannot instantiate the type T
}
public static <T extends A<T>> void process(T t)
{
// Do Something...
}
public class B extends A<B>
{
}
public class C extends A<C>
{
}
}
这里,类声明中的"new()"语法强制派生类编写一个默认构造函数,使得从基类调用"new T()"成为可能。换句话说,当我编写基类时,我确信派生类将具有默认构造函数,以便我可以从基类实例化派生类对象。
我在Java中的问题是,我不能从超类实例化派生类对象。我得到"Cannot instantiate the type T"
错误的"new T()"
调用。在Java中是否有类似的c#方式,或者我应该使用原型模式和克隆之类的东西?
Java不支持具体化泛型,所以没有等价的" new T();
"。我解决这个问题的方法是对类型标记使用反射。类型标记指示泛型类型是什么。
public abstract class A<T> {
private Class<T> typeToken;
// constructor
public A() {
typeToken = (Class<T>) ((ParameterizedType) getClass().getGenericSuperclass()).getActualTypeArguments()[0];
}
}
然后使用反射来实例化类。它是丑陋的,但它完成了工作。
你可以从这个链接中找到一些关于c#和Java泛型之间区别的解释-比较Java和c#泛型。
Java泛型是一个完全的编译时构造。对于以任何方式依赖于运行时信息的泛型类型参数,您都不能做任何事情。这包括:
- 创建泛型类型的实例参数。
- 创建泛型数组参数。 查询类的运行时类泛型类型参数
- 使用泛型instanceof参数。
您可以使用java.lang.reflect
命名空间绕过此限制。例如,请参阅这个stackoverflow问题:泛型和Class.forName()
另外,如果您正在使用泛型,请注意这一点。
T[] someArray = new T[];
这是首选ArrayList
而不是数组的原因之一。问题的原因在于可重构性和类型擦除。
使用bog标准抽象工厂模式。然后你得到额外的好处,你不需要绑定到一个特定的类型,实现类型不需要有一个特定的构造函数,实例可以有一些参数化,实例可以被缓存,等等。
看在上帝的份上,不要用反射
除了其他注释之外,我建议不要使用泛型。它们是不需要的——它们在编译时就会被剥离——如果你不太了解它们,你会试图让它们做它们不能做的事情。
一旦你的类正常工作,然后将它们重新添加进去。此时,您的IDE将给您提供许多有用且易于理解的建议,并且泛型将在您使用错误类的对象时警告您。
在我看来,这个类完成后可能根本不需要泛型。(我不知道这个类还能做什么,我也不明白静态方法的用法——它们永远不能访问单个实例的类型信息。)
实际上这在Java中不是问题。习惯用法是传递类
public static <T extends A<T>> T process(Class<T> clazz)
{
T o = clazz.newInstance();
process( o );
return o;
}
X x = process(X.class); // not too verbose
我添加了一个返回值来说明一般情况