【问题标题】:Generics and "where" clause泛型和“where”子句
【发布时间】:2009-11-04 00:44:12
【问题描述】:

在。 NET,我可以这样做:

public static T[] CreateAndFillArray<T>(int size) where T : new()
{
   T[] array = new T[size];
   for (int i = size - 1; i >= 0; i--)
       array[i] = new T();
   return array;
}

我们必须指定“where T : new()”子句。

如何在 Java 中做到这一点?

【问题讨论】:

标签: java generics where


【解决方案1】:

除非您将 T 作为参数传递,否则您将无法在 java 中创建泛型“T”类型的数组。

public static <T> T[] createAndFillArray(Class<T> cls, int size) {
  T[] result = (T[]) Array.newInstance(cls, size);
  for (int i=0; i<size; i++) {
    result[i] = cls.newInstance();
  }
  return result;
}

【讨论】:

  • 正确。与 C# 不同,Java 没有具体化的泛型。所以T 的实际类型不能用于构造新对象。
  • 有趣的是,CLR 实际上做了一个非常相似的技巧来做new T()(和typeof(T),以及其他一些它真正必须知道的类似事情T)——它添加了一个隐藏类型句柄的隐藏方法参数。唯一的区别是它在 C# 和 IL 级别上都不可见 - 它是运行时的实现细节。
【解决方案2】:

在 Java 中,不能实例化泛型数组。 (例如 new T[size] 不能工作) 这是因为在运行时泛型类型会丢失(“擦除”)并且无法恢复。

是否有不能使用的原因,例如 new ArrayList()?

【讨论】:

  • 当然,我可以使用ArrayList。我想知道我是否可以在 Java 中这样做;)
  • @mykhaylo:嗯? ArrayList Java 的一部分。并且ArrayList 不会解决您的所有问题,除非您将函数更改为接受ArrayList 作为参数。就像你不能做new T[size]一样,你也不能做new ArrayList&lt;T&gt;(),因为T实际上在运行时被删除了。
  • @Daniel - 你可以new ArrayList&lt;T&gt;();如果不将 Class&lt;T&gt; 作为参数传递,您将无法使用 T 的新实例填充它。
  • 解决不了他的问题,因为还没有new T()的类似物。他可以创建一个ArrayList,当然,但他不能用实例填充它。
【解决方案3】:

Java 没有等效的构造。包含构造函数的类没有编译时安全性。

您可以在运行时执行此操作,但您必须传递非空 T 或 Class 作为参数。实际使用的类型参数不会在运行时保留。

public static <T> T[] createAndFillArray(T sampleObject, int size) throws Exception {
        Class<T> klass = sampleObject.getClass();
        T[] arr = (T[]) Array.newInstance(klass, size);
        for (int i = 0; i < size; i++) {
            arr[i] = klass.newInstance();
        }
        return arr;
}

如果没有公共无参数构造函数,上述方法将起作用,但会引发异常。你不能让编译器强制要求有一个。

编辑:ChssPly76 击败了我,所以我修改了上面的代码,给出了一个示例,你传入一个实际的对象样本,只是为了展示它是如何完成的。通常在这种情况下,您会传入类,因为 sampleObject 最终不会出现在数组中。

【讨论】:

    【解决方案4】:

    您可以使用这个想法来解决其他答案中缺少编译时间检查的问题:

    import java.lang.reflect.Array;
    
    public class Main
    {
        public static void main(String[] args)
        {
            final String[] array;
    
            array = createAndFillArray(String.class, 10, new StringCreator());
    
            for(final String s : array)
            {
                System.out.println(s);
            }
        }
    
        public static <T> T[] createAndFillArray(final Class<T>   clazz,
                                                 final int        size,
                                                 final Creator<T> creator)
        {
            T[] result = (T[]) Array.newInstance(clazz, size);
    
            for (int i=0; i<size; i++)
            {
                result[i] = creator.newInstance();
            }
    
            return result;
        }
    }
    
    interface Creator<T>
    {
        T newInstance();
    }
    
    class StringCreator
        implements Creator<String>
    {
        public String newInstance()
        {
            // not the best example since String is immutable but you get the idea
            // you could even have newInstance take an int which is the index of the 
            // item being created if that could be useful (which it might).
            return ("hello");
        }
    }
    

    这实际上比您描述的 C# 方式更灵活,因为您可以根据需要控制构造函数,而不是简单地调用无参数的构造函数。

    【讨论】:

      【解决方案5】:

      您不能在 Java 中执行此操作,因为 Java 不支持结构类型检查。

      Scala 可以,但它比实现适当的接口要慢得多(因为它在内部使用反射来进行函数调用)。 Scala 不允许您对对象构造函数的形式设置约束。 JVM 使用类型擦除,因此泛型代码实际上并不知道它在操作什么类型,因此它无论如何也无法构造该类型的新对象。

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2016-04-27
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多