JDK-6260652 : (coll) Arrays.asList(x).toArray().getClass() should be Object[].class
  • Type: Bug
  • Component: core-libs
  • Sub-Component: java.util:collections
  • Affected Version: 5.0,6
  • Priority: P3
  • Status: Closed
  • Resolution: Fixed
  • Submitted: 2005-04-25
  • Updated: 2018-07-18
  • Resolved: 2015-07-09
The Version table provides details related to the release that this issue/RFE will be addressed.

Unresolved : Release in which this issue/RFE will be addressed.
Resolved: Release in which this issue/RFE has been resolved.
Fixed : Release in which this issue/RFE has been fixed. The release containing this fix may be available for download as an Early Access Release or a General Availability Release.

To download the current JDK release, click here.
JDK 9
9 b73Fixed
Related Reports
Duplicate :  
Relates :  
Relates :  
Relates :  
Sub Tasks
JDK-8176893 :  
Description
FULL PRODUCT VERSION :
java version "1.5.0_02"
Java(TM) 2 Runtime Environment, Standard Edition (build 1.5.0_02-b09)
Java HotSpot(TM) Client VM (build 1.5.0_02-b09, mixed mode, sharing)


ADDITIONAL OS VERSION INFORMATION :
Linux itppc27 2.4.19-4GB #1 Mon Aug 4 23:38:42 UTC 2003 i686 unknown

A DESCRIPTION OF THE PROBLEM :
The Collection documentation claims that

    collection.toArray()

is "identical in function" to

    collection.toArray(new Object[0]);

However, the implementation of Arrays.asList does not follow this: If created with an array of a subtype (e.g. String[]), its toArray() will return an array of the same type (because it use clone()) instead of an Object[].

If one later tries to store non-Strings (or whatever) in that array, an ArrayStoreException is thrown.

Either the Arrays.asList() implementation (which may return an array of component type not Object) or the Collection toArray documentation (which does not allow argumentless toArray() to return arrays of subtypes of Object) is wrong.

STEPS TO FOLLOW TO REPRODUCE THE PROBLEM :
Execute code.

EXPECTED VERSUS ACTUAL BEHAVIOR :
EXPECTED -
[Ljava.lang.Object;@10b62c9
[Ljava.lang.Object;@82ba41

ACTUAL -
[Ljava.lang.String;@10b62c9
[Ljava.lang.Object;@82ba41


REPRODUCIBILITY :
This bug can be reproduced always.

---------- BEGIN SOURCE ----------
import java.util.*;


public class ToArray
{
    public static void main(String[] args)
    {
        List l = Arrays.asList(args);

        System.out.println(l.toArray());
        System.out.println(l.toArray(new Object[0]));
    }
}

---------- END SOURCE ----------

CUSTOMER SUBMITTED WORKAROUND :
Only use toArray(Object[]).
###@###.### 2005-04-25 12:45:46 GMT

Comments
For history/informational purposes, an incompatibility has been spotted in the field, with hive-metastore-2.1.1: https://stackoverflow.com/questions/51372788/array-cast-java-8-vs-java-9 No further action requested.
18-07-2018

Several messages down in the review thread, Paul Sandoz made this note: ========== I trawled through the JDK code and found one other violation of the toArray contract in: com.sun.java.util.jar.pack.ConstantPool.Index: public Entry[] toArray() { return toArray(new Entry[size()]); } ========== http://mail.openjdk.java.net/pipermail/core-libs-dev/2015-June/034413.html The context is a static class Index which extends AbstractList<Entry>. This is a bit suspicious at first glance. This class is providing a covariant override of the no-arg toArray(). Presumably callers of this class are expecting to get an Entry[] when they call this method, so it seems like that ought to be fine. However, this violates the Liskov Substitution principle. If an instance of Index were to escape to code that treats it as a List<?>, a caller might call toArray() and expect to be able to store an Object (or some non-Entry type) into it. This would fail. It seems unlikely that this could ever happen, though, as this Index class is part of the Pack200 internal implementation and is not exposed to applications. JDK-8130732 was filed to track this issue.
22-05-2018

Our backport of this change to jdk8 caused 5 test failures in our (very large) test suite, all due to test (i.e. non-production) code doing dumb things like List x = ... .asList("a", "b", "c"); String[] y = x.toArray(); where redeclaring as Object[] would have been fine, but more generally, calling toArray at all is almost always a sign that the test code could have been written much simpler, by either using the List or the original input array.
13-08-2015

@Martin, if everything happens to work, please leave that as a positive affirmation in a comment! Thanks.
20-07-2015

A backport of this to jdk8 has been committed as a local change at Google, so there will be lots of compatibility testing before jdk9 is released.
20-07-2015

Core libs review thread: http://mail.openjdk.java.net/pipermail/core-libs-dev/2015-June/034355.html
16-07-2015

10 years later, I still believe this is "just" a bug that should be fixed.
26-06-2015

This is a long standing issue and what I can tell, there haven't been too many reports of it. I don't see any strong reason to try to squeeze this into jdk8 so deferring it to early jdk9 seems reasonable (to me).
28-09-2013

A testing datapoint, the current jtreg jdk_core regression suite runs successfully when the toArray method is changed to: return Arrays.copyOf(a, 0, a.length, Object.class); This is by no means exhaustive but perhaps a useful indication that this change might be safe. Nonetheless, any objections to deferring this from 8?
27-09-2013

SUGGESTED FIX --- /u/martin/ws/mustang/src/share/classes/java/util/Arrays.java 2005-08-11 11:04:21.718074000 -0700 +++ /u/martin/ws/toArray/src/share/classes/java/util/Arrays.java 2005-08-27 18:01:03.522592000 -0700 @@ -2949,7 +2948,7 @@ implements RandomAccess, java.io.Serializable { private static final long serialVersionUID = -2764017481108945198L; - private Object[] a; + private final E[] a; ArrayList(E[] array) { if (array==null) @@ -2962,17 +2961,27 @@ } public Object[] toArray() { - return (Object[])a.clone(); + return Arrays.copyOf(a, a.length, Object[].class); + } + + public <T> T[] toArray(T[] a) { + int size = size(); + if (a.length < size) + return Arrays.copyOf(this.a, size, (Class<T[]>) a.getClass()); + System.arraycopy(this.a, 0, a, 0, size); + if (a.length > size) + a[size] = null; + return a; } public E get(int index) { - return (E)a[index]; + return a[index]; } public E set(int index, E element) { - Object oldValue = a[index]; + E oldValue = a[index]; a[index] = element; - return (E)oldValue; + return oldValue; } public int indexOf(Object o) {
07-08-2005

EVALUATION The submitter is correct. toArray() must always return an object of precise type Object[].
07-08-2005