There is code in item32 in effective java
public class Item32 {
static <T> T[] toArray(T ... args){
return args;
}
public static void main(String[] args) {
String[] attributes = toArray("1","2","3"); // no problem in Java 17 but problem in Java8
}
}
As there is type erasure, I expect that result of toArray("1","2","3") is Object[], so there is a ClassCastException. However, there is no exception for Java 17 and 11. But in Java 8, there is an exception. What happens in Java 17?
Type erasure is working here, it's just hard to see. It's also slightly complicated by the fact that arrays can have real subtypes: String[] is something you can check for a runtime.
Let's break down the elements here:
toArray method is indeed type-erased, and so the String[] attributes = introduces an implicit cast (as always happens with type erasure).T being a String, it creates the array as a String[] — which is a subtype of Object[].toArray, which treats it only as an Object[] and returns it back.String[], which succeeds due to how the call site created the array to begin with.You would presumably not be surprised by something like this:
public static <T> T echo(T item) {
return item;
}
Long i = echo(1L);
And you might further not be surprised if that call site were instead:
String[] a = echo(new String[0]);
The same thing is happening in your code.
You can see this if you use javap -c to look look at the bytecode. The relevant part is the end of the main method:
34: invokestatic #9 // Method toArray:([Ljava/lang/Object;)[Ljava/lang/Object;
37: checkcast #10 // class "[Ljava/lang/String;"
40: astore_2
41: return
In Effective Java (Third Edition) Item 32 it says below the definition of the toArray method:
The type of this array is determined by the compile-time types of the arguments passed in to the method [...]
This is also what you are seeing; you are calling the method with three Strings and therefore a String[] is created.
This behavior is described as well in the Java Language Specification; unfortunately it just refers to the "formal parameter [of a method]", while it actually seems to mean the inferred type, in your case String[].
You are right that type erasure applies and the T of toArray is Object at runtime. However, because the array storing the arguments is implicitly created by the caller (as described above), the array has the type which was inferred at compile-time.
Most likely the ClassCastException occurred when you modified the code so that at compile time the argument of T could not be inferred as String anymore. As pointed out in the comments, in the code sample from Effective Java this occurs when another generic method named pickTwo is added which calls toArray.
In case you really experience a ClassCastException with exactly the code snippet you included in your question, it would be good if you shared information about the compiler you are using because that compiler might then have a bug.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With