JDK-7023484 : add typesafe static Collections.toArray(Collection, IntFunction)
  • Type: Enhancement
  • Component: core-libs
  • Sub-Component: java.util:collections
  • Priority: P4
  • Status: Open
  • Resolution: Unresolved
  • Submitted: 2011-03-01
  • Updated: 2024-04-11
Related Reports
Relates :  
Relates :  
Description
A DESCRIPTION OF THE REQUEST :
java.util.Collection defines method <T> T[] toArray(T[] a);

Unfortunately, this is not compile-time type-safe, which can be seen in the test case below. The problem is that the signature of the method doesn't refer to the type parameter <E> of Collection.

The correct signature of toArray(..) would be something like this, but this is not valid in Java: <T super E> T[] toArray(T[] a);

Since I don't think Collection#toArray(T[]) can be fixed, I suggest a type-safe helper method in the java.util.Collections class:

	/**
	 * Returns an array containing all of the elements in the given collection. This is a
	 * compile-time type-safe alternative to {@link Collection#toArray(Object[])}.
	 *
	 * @param collection the source collection
	 * @param clazz the type of the array elements
	 * @param <A> the type of the array elements
	 * @return an array of type <code>A</code> containing all of the elements in the given
	 *         collection
	 * @throws NullPointerException if the specified collection or class is null
	 */
	public static <A> A[] toArray(Collection<? extends A> collection, Class<A> clazz) {
		Object array= Array.newInstance(clazz, collection.size());
		@SuppressWarnings("unchecked")
		A[] typedArray= collection.toArray((A[]) array);
		return typedArray;
	}



JUSTIFICATION :
1.6 code like this compiles fine but throws an ArrayStoreException at run time:
        Integer[] ints = nums.toArray(new Integer[nums.size()]);

With the suggested method, clients see the type safety problem at compile time:
        Integer[] ints = Collections2.toArray(nums, Integer.class);

An added benefit of the new method is that the client doesn't have to write boilerplate code to create an array of a specific type.


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

public class ArrayTypeBreak {
    public static void main(String[] args) {
        Collection<Number> nums = new ArrayList<Number>();
        nums.add(new Integer(1));
        nums.add(new Long(-1));
        
        Object[] objs = nums.toArray(new Object[0]);
        Object[] objs2 = nums.toArray(new Number[0]);
        Number[] nums2 = nums.toArray(new Number[0]);

        // No compile-time warning, but throws ArrayStoreException:
        Integer[] ints = nums.toArray(new Integer[nums.size()]);
        
        for (Integer integer : ints) {
            System.out.println(integer);
        }
    }
}
---------- END SOURCE ----------

CUSTOMER SUBMITTED WORKAROUND :
Clients can use a home-made helper method. However, such basic functionality should be shared in the JRE.

Comments
The "safeToArray" fails for the same reason as given in JDK-5063030. That is, the type parameter T can be inferred to be a supertype of the array component type, because arrays are covariant. Given this, the construct can still compile without any warnings yet generate an ArrayStoreException at runtime. Consider: List<String> list = List.of("a", "b", "c"); Object[] array = safeToArray(list, Integer[]::new); Thus, the type parameter T doesn't provide the desired type constraint between the two method arguments. The compiler does issue an error if a tighter target type is provided, e.g., if the local variable to receive the assignment is Integer[] instead of Object[]. However, this isn't possible in general, for example if the return value of safeToArray() is passed as an argument to a method taking Object[]. In such cases, ArrayStoreException can still occur, and so this sort of method isn't actually type-safe.
11-04-2024

Check this for type safety against the example given in JDK-5063030.
08-12-2017

A more modern approach to this would be something like the following: static <T> T[] safeToArray(Collection<? extends T> c, IntFunction<T[]> generator) { return c.toArray(generator); } This establishes the proper static typing relationship between the element type of the collection and the component type of the generated array.
06-12-2017