【问题标题】:Are there any generic version of Array.newInstance?是否有任何通用版本的 Array.newInstance?
【发布时间】:2013-07-18 05:27:09
【问题描述】:

我注意到在 Java 中Array.newInstance() 返回Object,而不是T[]。可以理解,这种方法是在 Java 支持泛型之前引入的。

但是令人惊讶的是,没有等效的通用版本。 Java 7 的 Arrays.copyOf 不一样 - 它复制参数的内容,而不是创建一个新的虚拟数组(其中包含所有空对象)。

既然这个实现看起来微不足道,有什么理由不将它添加到 JRE 中吗?还是我找不到?

更新

看来我应该提供我自己的“微不足道”的实现来避免对这个问题的误解。

class MyArrayUtil {
    //Generic version for classes
    @SuppressWarnings("unchecked")
    public static <T> T[] newArrayOf(T[] t, int len){
        return (T[]) Array.newInstance(t.getClass().getComponentType(), len);
    }
    //For array of int
    public static int[] newArrayOf(int[] t, int len){
        return new int[len];
    }
    //For other primitive types...
}

我没有发布此代码作为答案,因为它不是问题的答案。问题在于原因和/或现有代码,而不是如何实现。

更新

我已经更新了代码,使其类似于Arrays.copyOf,优点是程序员可以简单地更改参数的类型来调整代码为另一种类型。我还取消了对原始类型使用Array.newInstance

【问题讨论】:

  • 不,不可能以编译时类型安全的方式实现这一点。例如,您不能创建 a 的新实例:List&lt;T&gt;[]。编译器永远不会知道T的类型是什么,所以它不能保证客户端代码是安全的。
  • 既然Arrays.copyOf 可以完成这项工作,没有明显的理由你不能。
  • Arrays.copyOf 确定要复制的数组的具体类型。
  • 所以有一个类似的Arrays.newInstanceOf 使用具体类型来提供类型信息很好,正如我在问题中所说的那样,实现是微不足道的。
  • fwiw,Guava 提供just such a method

标签: java arrays generics


【解决方案1】:

Guava 提供just such a function。这不是 Guava(或 Apache Commons)第一次提供 JDK 没有的常用帮助器,无论出于何种原因。

您可能知道这一点,但是对于将来偶然发现这一点的 googler 的一些背景知识:签名不能通用的原因是方法 Array.newInstance returned Object in Java 1.4,因此为了向后兼容,原始该方法的版本也应该返回Object。如果它被泛化为:

<T> T[] newInstance(Class<T> componentType, int length)

...那么返回类型将是Object[],而不是Object。这会破坏向后兼容性,Java 设计人员一直非常努力地不这样做。

Arrays.copyOf 方法只出现在 Java 1.6 中,因此不必担心向后兼容性。

【讨论】:

  • 现在我想我明白为什么让newArray 支持原始类型没有多大意义了。如果你想创建一个不是泛型类型参数的新数组,你可以简单地使用new XYZ[]。如果不是这种情况,您必须使用类类型,并且只有在这种情况下,newArray 工具才有用。
  • 如果您在方法签名/类定义中需要类似的参数/字段,这应该不是问题。
  • @EarthEngine 此外,它确实适用于原始类型,因为存在int.class(对于其他原始类型也是如此)。有关示例,请参见 ideone.com/aTVbJx
  • 我知道这是可能的。但只有在运行时有 Class&lt;?&gt; 实例时才有意义。在这种情况下,您的代码明确使用反射,而不仅仅是泛型。
  • 由于将擦除的返回类型从Object 更改为Object[] 是一种缩小范围,因此至少在理论上应该是二进制兼容的。
【解决方案2】:

Array.newInstance() 不能像&lt;T&gt; T[] newInstance(Class&lt;T&gt; class, int size) 那样声明的基本原因是因为它可以用来创建基元数组。例如,如果你传入int.class,它的类型为Class&lt;Integer&gt;,那么从声明中你会期望它返回一个Integer[] 对象。但是,该函数实际上返回了一个int[] 对象,它不是Integer[] 的子类型,所以它的类型是错误的。

当然,他们可以添加一个额外的方法,例如 newReferenceArrayInstance(),它禁止原始类型(例如,在传递原始类型时抛出异常),因此可以安全地声明为返回 T[]。但是,这似乎只是为了避免未经检查的强制转换而添加了一个完全冗余的方法。

【讨论】:

    【解决方案3】:

    不,您需要以一种或另一种方式将具体类型作为参数传递。正如您所提到的,Arrays.copyOf 展示了这一点。

    Class<T> type = determinteTheTypeSomehow();
    return (T[]) Array.newInstance(type, length);
    

    【讨论】:

    • Array.newInstance 需要Class&lt;?&gt;,因为Arrays.copyOf 需要T[],所以它们确实需要额外的参数而不是类型参数。我只是想知道为什么我们不能有一个接受Class&lt;T&gt;T[]Arrays.newInstance(仅作为模板;内部的内容被忽略)。
    • 是的,我理解,但是您正试图根据您的需要改变 api,这将永远无法工作;)我承认这可能令人沮丧,但事实就是如此。
    • 当我编写泛型类型时,我经常将具体参数类型作为参数传递给构造函数,用于此(和其他)目的。
    猜你喜欢
    • 2010-12-27
    • 1970-01-01
    • 2022-08-04
    • 2016-07-15
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多