11

In short, this won't compile:

public <A> void test() {
    A[] temp = new A[]{};
}

Is it because of problems with backward compatibility, or is it something fundamental in the language design that prevents it?

pgsandstrom
  • 13,733
  • 12
  • 68
  • 102

5 Answers5

7

The bottom line is that the class that represents the array has to know the component type. Hence the method on the Class object:

public Class<?> getComponentType()
Returns the Class representing the component type of an array. If this class does not represent an array class this method returns null.

So when you would try:

 A[] a = new A[0];

At compile time, it's obvious that we don't know the type, as it's a generic parameter. At runtime, we don't know the type due to type erasure. So instantiating the array is impossible.

Think of the above statement as equivalent to:

 A[] a = (A[])Array.newInstance(???, 0);

And due to type erasure, we can't get the class of A at runtime.

It was asked why not have the compiler reduce to Object[] or Number[] or something like that?

It's because a different Class will be returned depending on the component type. So:

 new Object[0].getClass() 
 new Integer[0].getClass()

are not the same class. In particular the "getComponentType()" method on the class would return different values.

So if you reduce it to Object[] instead of A[], you're not actually getting back something of type A[], you're getting back Object[]. Object[] cannot be cased to Integer[] and will yield a ClassCastException.

Matt
  • 10,997
  • 2
  • 19
  • 32
5

Type erasure is the word you are looking for. It basically means that the generic information is erased at compile time. The main reason for this is backward compatibility. Older programs should still run on the new java virtual machine.

dvberkel
  • 653
  • 4
  • 15
3

In Java, the type system for arrays and generics is incompatible. There are two main areas of discrepancy: dynamic vs static type check and covariance.

Generics are statically checked: that is, the compiler makes sure that the type definitions are coherent. "Type erasure" was a compromise to ensure backwards-compatibility in the JVM. After compilation, generic type definition is no longer available. E.g. List<T> becomes List.

In contrast, arrays are dynamically type-checked. Consider the following example:

String strings[] = {"a","b","c"};
Object simple[] = strings;
simple[0] = new Object(); // Runtime error -> java.lang.ArrayStoreException 

Covariance is the inheritance relationship of a container based on the content. Given types A,B and a container C, if B isAssignableFrom(A) => C isAssignable C.

Arrays in Java are covariant, in the previous example, Given that Class<Object>.isAssignableFrom(Class<String>) => Object[] is assignable from String[]

In contrast, generic types are not covariant in Java. Using the same example:

List<String> stringList = new ArrayList<String>();
List<Object> objectList = stringList; // compiler error - this is not allowed.

Given the type erasure of the generics implementation, type information is lost in translation and therefore the dynamic type check would be compromised if you could create an array of a generic type.

Further read on the complications and implications of these issues: Arrays in Java Generics

Hearen
  • 6,019
  • 2
  • 36
  • 50
maasg
  • 35,926
  • 11
  • 83
  • 112
1

This doesn't work for the same (or almost the same) reason that new A() can't work: you are expecting the compiler to know the runtime type of A, which it clearly cannot know. This would work if Java Generics were like C++ templates, where new code is generated for each instantiation of the template involving A.

Marko Topolnik
  • 179,046
  • 25
  • 276
  • 399
0

Yes, there is a fundamental reason, which boils down to type erasure.

Consider the following snippet:

A[] temp = new A[5];       // Assume this would compile.

Object[] objects = temp;   // This is allowed, since A extends Object.

objects[0] = new String(); // This does *not* throw an ArrayStoreException
                           // due to type erasure since the original type of A
                           // is now Object.

A t = temp[0];             // Now this seemingly innocent line would *fail*.

Related question:

Community
  • 1
  • 1
aioobe
  • 383,660
  • 99
  • 774
  • 796