Java:创建一个类型为类型参数的对象

7

我想编写与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中,我的问题是无法从超类实例化一个派生类对象。当我调用"new T()"时,会出现"Cannot instantiate the type T"的错误。是否有类似于C#的方法在Java中使用,或者我应该使用原型模式和克隆等方式?
6个回答

5

Java不支持具体化的泛型,因此没有相当于"new T();"的等效方法。我解决这个问题的方式是使用反射来针对类型标记进行操作。类型标记指示了泛型类型是什么。

public abstract class A<T> {
  private Class<T> typeToken;
  // constructor
  public A() {
        typeToken = (Class<T>) ((ParameterizedType) getClass().getGenericSuperclass()).getActualTypeArguments()[0];
  }
}

然后使用反射来实例化类。虽然不太美观,但它能完成工作。


感谢您的快速回复。由于我正在尝试在静态方法内创建一个新实例,因此无法进行getClass()调用。此外,由于编译时构造,T.class不存在。因此,反射对我来说似乎不是一个解决方案。或者我还没有找到它(: - Mehmet Ataş
只有子类使用具体类型参数才能正常工作。例如,如果子类是public class B<T> extends A<T>,那么这个问题仍然存在。我认为最好使用类型标记,但要求调用者将标记传递到构造函数或方法中。 - Mark Peters

2
您可以从此链接 - 比较Java和C#泛型中找到一些关于C#和Java泛型之间区别的解释。

Java泛型是完全在编译时构建的。您不能对依赖于任何运行时信息的泛型类型参数执行任何操作,包括:

  • 创建泛型类型参数的实例。
  • 创建泛型类型参数的数组。
  • 查询泛型类型参数的运行时类。
  • 使用instanceof与泛型类型参数。

您可以通过java.lang.reflect命名空间绕过此限制。例如,请参见此stackoverflow问题:Genercs and Class.forName()


0

如果你正在使用泛型,请注意这一点。

T[] someArray = new T[];

这是偏爱使用ArrayList而不是数组的一个原因。问题的原因在于具体化和类型擦除。


0

实际上这在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

我添加了一个返回值来说明一般情况。


0

只需使用标准的抽象工厂模式即可。这样,您将获得额外的好处,不会绑定到特定类型,实现类型不需要具有特定的构造函数,实例可以进行一些参数化,实例可以被缓存等等。

求求你了,不要使用反射。


0
除了其他评论外,我建议不要使用泛型。它们不是必需的--它们在编译时会被剥离--如果您对它们不熟悉,您将试图让它们做一些它们无法实现的事情。
一旦您的类正常工作,然后再将它们添加回来。此时,您的IDE将为您提供大量有用且易懂的建议,而泛型将在您使用错误类的对象时发出警告。
在我看来,完成此类时可能根本不需要泛型。(我不知道这个类可能还会做什么,也不理解静态方法的用途--它们永远不会获取单个实例的类型信息。)

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