在泛型方法中创建通用数组实例

时间:2010-12-02 16:19:33

标签: java arrays

我正在尝试构建一个帮助方法,将两个行列表转换为一个数组转换为一行。我遇到的问题是我不确定如何创建一个T []实例。

我试过

Array.newInstance(T.class, list.size)但是我无法提供它.T.class ..

还尝试new T[](list.size),但它不喜欢参数。

public <T> T[] ConvertToArray(List<T> list)
{
   T[] result = ???

   result = list.toArray(result);

   return result;
}

还有其他想法吗?

由于

5 个答案:

答案 0 :(得分:14)

您不能将泛型和数组混合在一起。泛型具有编译时检查,数组具有运行时检查,并且这些方法大多不兼容。起初我建议这样做:

@SuppressWarnings("unchecked")
public <T> T[] ConvertToArray(List<T> list)
{      
   Object[] result = new Object[list.size()];
   result = list.toArray(result);
   return (T[])result;
}

这是一种隐秘的错误,因为至少有一个其他人认为它会起作用!但是,当您运行它时会出现不兼容的类型错误,因为您无法将Object []强制转换为Integer []。为什么我们不能获得T.class并创建一个正确类型的数组?或者new T[]

泛型使用类型擦除来保持向后兼容性。它们在编译时检查,但从运行时剥离,因此字节码与预泛化JVM兼容。这意味着您无法在运行时获得泛型变量的类知识!

因此,虽然您可以提前保证T[] result的类型为Integer [],但代码list.toArray(result);(或new T[]Array.newInstance(T.class, list.size());)只会在运行时发生,它无法知道T是什么!

这是 工作的版本,作为阅读该讲座的奖励:

public static <T> T[] convertToArray(List<?> list, Class<T> c) {
    @SuppressWarnings("unchecked")
    T[] result = (T[]) Array.newInstance(c, list.size());
    result = list.toArray(result);
    return (T[]) result;
}

请注意,我们有第二个参数在运行时(以及通过泛型编译时)提供类。你可以像这样使用它:

Integer[] arrayOfIntegers = convertToArray(listOfIntegers, Integer.class);

这值得麻烦吗?我们仍然需要压制警告,所以它绝对安全吗?

我的回答是肯定的。生成的警告只是来自编译器的“我不确定”。通过单步执行,我们可以确认该转换将始终成功 - 即使您将错误的类作为第二个参数,也会抛出编译时警告。

这样做的主要优点是我们已将警告集中到一个地方。我们只需要证明这一个地方是正确的,我们知道代码将永远成功。引用Java文档:

  

该语言旨在保证如果使用javac -source 1.5编译整个应用程序而没有未经检查的警告,则它是类型安全的[1]

所以现在不是在你的代码中都有这些警告,它只是在一个地方,而你可以使用它而不必担心 - 通过使用它会大大降低你犯错误的风险。

你可能还想看一下this SO answer更深入地解释这个问题,this answer这是我写的时候的床单。除了already cited Java documentation之外,我使用的另一个方便的参考是Neal Gafter的this blog post,他是Sun Microsystems的高级工程师,以及1.4和5.0语言功能的联合设计师。

当然,感谢ShaneC正确地指出我的第一个答案在运行时失败了!

答案 1 :(得分:2)

如果你无法通过T.class,那么你基本上是搞砸了。类型擦除意味着您在执行时根本不知道T的类型。

当然还有其他指定类型的方法,例如super type tokens - 但我的猜测是,如果你无法传入T.class,你将无法传入一个类型令牌。如果可以,那就太好了:))

答案 2 :(得分:1)

问题在于,由于类型擦除,List在运行时不知道它的组件类型,而组件类型是创建数组所需的类型。

因此,您可以在API中找到两个选项:

我能想到的唯一其他可能性是一个巨大的麻烦:

  • 迭代列表中的所有项目,找到“最大公约数”,这是所有项目扩展或实现的最具体的类或接口。
  • 创建该类型的数组

但是这将比你的两行更多(并且它也可能导致客户端代码做出无效的假设)。

答案 3 :(得分:0)

这是否真的需要改为单行?对于任何具体类,您可以在一行中执行List to Array转换:

MyClass[] result = list.toArray(new MyClass[0]);

当然,这对于类中的泛型参数不起作用。

参见Joshua Bloch的Effective Java Second Edition,第25项:首选列表到阵列(第119-123页)。这是sample chapter PDF的一部分。

答案 4 :(得分:-1)

这是我能看到的最接近你想要的东西。这使得您在编写代码时知道类的大假设以及列表中的所有对象都是同一个类,即没有子类。我确信这可以变得更复杂,以减轻没有子类的假设,但我不知道如何在Java中你可以绕过在编码时知道类的假设。

package play1;

import java.util.*;

public class Play
{
  public static void main (String args[])
  {
    List<String> list=new ArrayList<String>();
    list.add("Hello");
    list.add("Shalom");
    list.add("Godspidanya");

    ArrayTool<String> arrayTool=new ArrayTool<String>();
    String[] array=arrayTool.arrayify(list);

    for (int x=0;x<array.length;++x)
    {
      System.out.println(array[x]);
    }
  }
}
class ArrayTool<T>
{
  public T[] arrayify(List<T> list)
  {
    Class clazz=list.get(0).getClass();
    T[] a=(T[]) Array.newInstance(clazz, list.size());
    return list.toArray(a);
  }
}