【问题标题】:Java: Create an object whose type is a type parameterJava:创建一个类型为类型参数的对象
【发布时间】:2011-09-29 14:52: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# 类似的方法,或者我应该使用原型模式和克隆之类的方法吗?

【问题讨论】:

    标签: c# java generics inheritance


    【解决方案1】:

    Java 不支持reified generics,因此没有与“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 不存在。所以,反思对我来说似乎不是一个解决方案。或者我还没有找到它(:
    • 这仅在子类使用具体类型参数时才有效。例如,如果子类是public class B&lt;T&gt; extends A&lt;T&gt;,这将有完全相同的问题。我发现使用类型标记更好,但要求调用者将标记传递给构造函数或方法。
    【解决方案2】:

    您可以从this li nk - comparing java and C# generics 找到一些关于 C# 和 Java 中泛型之间区别的解释。

    Java 泛型是一个完全编译时的构造。您不能对以任何方式依赖运行时信息的泛型类型参数做任何事情。这包括:

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

    您可以使用java.lang.reflect namepsace 绕过此限制。例如查看这个 stackoverflow 问题:Genercs and Class.forName()

    【讨论】:

      【解决方案3】:

      另外,如果您使用泛型,请注意这一点。

      T[] someArray = new T[];
      

      这是更喜欢 ArrayList 而不是数组的原因之一。问题的原因在于可重构性和类型擦除。

      【讨论】:

        【解决方案4】:

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

        看在上帝的份上,不要使用反射。

        【讨论】:

          【解决方案5】:

          除了其他 cmets,我建议不要使用泛型。它们不是必需的——无论如何它们在编译时都会被剥离——如果你对它们不太了解,你会尝试让它们做它们不能做的事情。

          一旦你的类工作正常,然后重新添加它们。你的 IDE 会在那时给你很多有用和易懂的建议,当你使用泛型时,泛型会警告你使用错误类的对象。

          在我看来,这个类在完成后根本不需要泛型。 (我不知道这个类还能做什么,也不了解静态方法的使用——它们永远无法访问单个实例的类型信息。)

          【讨论】:

            【解决方案6】:

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

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

            【讨论】:

              猜你喜欢
              • 2013-06-27
              • 1970-01-01
              • 1970-01-01
              • 1970-01-01
              • 1970-01-01
              • 2013-05-17
              • 1970-01-01
              • 1970-01-01
              • 2016-12-05
              相关资源
              最近更新 更多