【问题标题】:why does List<String>.toArray() return Object[] and not String[]? how to work around this?为什么 List<String>.toArray() 返回 Object[] 而不是 String[]?如何解决这个问题?
【发布时间】:2011-12-16 02:55:18
【问题描述】:

有人知道为什么 Java 1.6 有这种行为吗:

List<String> list = ArrayList<String>();
String[] arr = (String[]) list.toArray();

我得到一个 ClassCastException,因为它返回 Object[] 而不是 String[]。

我以为 List&lt;T&gt;.toArray() 应该返回 T[] - 不是吗?有谁知道为什么语言中存在这种不便? 还有如何解决这个问题?如何从 List&lt;String&gt; 获取 String[] 而无需遍历项目?

【问题讨论】:

标签: java generics


【解决方案1】:

您需要传入一个数组,以便toArray 可以将其运行时类型用作提示。请改用toArray(new String[0])。您也可以传入一个预先确定大小的数组。

要理解,请考虑需要执行什么类型的擦除

new T[4]

工作。如果 Java 允许这样做,它可以做的最好的删除后是

new Object[4]

大多数toArray 实现使用java.lang.reflect.Array 构造一个正确类型的输出数组,给定作为Class 传递的类型提示。

【讨论】:

  • 谢谢你,迈克,我没有注意到这个方法 T[] toArray(T[] arg0) - 它返回我需要的东西。
  • 如果您想避免分配大小为 0 的数组,请使用 toArray(new String[list.size()])
  • 所以下面的答案说List&lt;E&gt; 接口不能提供E[] toArray() 方法,因为它会与旧的Object[] toArray() 方法冲突,该方法在1.2 中引入并且需要向后兼容.这是有道理的,因为您不能有两个仅因返回类型而异的方法签名。但是您的回答暗示 E[] toArray() 由于类型擦除而无法工作-我没看错吗?或者您使用T 作为泛型类型和使用E 作为声明的List 接口之间是否存在细微差别?
  • 对,我想我错过了显而易见的事情。像E get(int index) 这样的列表方法之所以有效,是因为它们在编译时强制执行,而不必在运行时知道或构造类型。 E[] toArray() 可以有它的返回值赋值/通过/等。在运行时检查,但实际构造数组需要知道正确的类型。
【解决方案2】:

因为数组从一开始就出现在 Java 中,而泛型仅在 Java 5 中引入。而 List.toArray() 方法是在 Java 1.2 中引入的,在泛型存在之前,因此指定它返回 Object[]。许多现有代码现在期望 List.toArray() 返回 Object[],因此现在无法更改。

此外,泛型在运行时会被删除,因此 ArrayList 甚至无法构造正确类型的数组。

这个漏洞是List.toArray(T[]) 方法,它会返回一个正确类型的数组,只要你给它一个正确类型的数组,让它知道使用什么类型。

【讨论】:

  • "因此它被指定返回 Object[]。许多现有代码现在期望 List.toArray() 返回 Object[],因此现在无法更改。废话。 Java 集合方法的返回类型已针对泛型进行了更改。这完全无关紧要。实际原因是不同类型的数组在运行时是不同的,因此它不可能返回正确类型的数组。在泛型之前和之后都是如此。
  • 丹尼尔,认真的吗?这个问题已经被问了多少次了?一百万?你还教我回答重复的问题?
  • @irreputable:当我昨天回答这个问题时,我没有立即看到(也没有临时记得)一个完全相同的副本。今天快速搜索出现了this one,我认为它已经足够接近了。如果您确信某个问题是重复的,那么与其假设是恶意的,为什么不直接投票关闭自己呢?你有足够的代表。
  • @newacct:我不确定这是否完全无关。诚然,Java 5 还引入了返回类型协方差,因此返回 T[] 而不是 Object[] 不一定是一项重大更改。但是由于数组方差的工作方式很奇怪:返回T[] 是否正确?我的一般经验法则是,您可以使用泛型,或使用数组协方差,但应避免同时使用两者。
  • @Daniel:返回类型协方差(处理覆盖方法的返回类型)在这里不是问题,因为其所有超类(集合)中的 toArray() 方法也将更改为 @ 987654333@。至于你关于它是否正确的问题,假设一个方法可以神奇地返回一个 T[],在大多数情况下它是正确的,因为T[]Object[] 的子类,所以任何期望@987654336 的人@应该可以取T[]。唯一的例外是如果他们试图将一个元素插入到不同类型的数组中,这是一种极不寻常的可能性。
【解决方案3】:

要了解为什么会发生这种情况,只需查看 Collection.toArray() 的 javadoc,然后查看 Collection.toArray(T[] a) 方法。还要记住,Java 中的泛型在运行时会被删除。

【讨论】:

    【解决方案4】:

    原因有两个:

    1. 该方法在泛型之前,它们无法在不破坏向后兼容性的情况下更改签名。

    2. 无论如何,由于List 是在没有Class&lt;T&gt; 的情况下构造的,因此实现无法构造T 类型的数组。

    【讨论】:

    • 你会把签名改成什么?
    • 你的意思是返回类型 T[]?如果你尝试使用它,你会得到一个 ClassCastException,因为该方法仍然会创建一个 Object[]
    • @newacct 当您更改方法签名时,通常会更改实现以匹配。但是请参阅我的编辑。
    • 我的反对意见是,Java api 中的所有方法签名确实 为泛型更改(例如所有集合类的方法等)。所以你声称它不能改变,因为它曾经是那样的,这是不正确的。加上 T[] 是 Object[] 的子类,因此如果将 Object[] 更改为 T[],它不会“破坏”任何期望 Object[] 的代码
    • @newacct (a) 如果这是您的反对意见,那么您花了很长时间才说清楚,即 3 个单独的答案; (b) 没有以向后不兼容的方式更改任何 Collections 方法:例如 Map.get()。
    【解决方案5】:

    解决方法是使用:

           List<String> list = new ArrayList<>();
             list.add("Sputnik");
             list.add("Explorer");
    
            //String[] str = list.toArray(); 
    

    这势必会抛出:不兼容的类型:Object[] 无法转换为 String[] 错误

           Instead use:
    
            String[] str = list.toArray(new String[0]);
            or
            String[] str = list.toArray(new String[list.size()]);
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2014-07-27
      • 2010-10-26
      • 1970-01-01
      • 2018-10-29
      • 1970-01-01
      • 2011-09-04
      相关资源
      最近更新 更多