Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Effective java item 32 issues : Type erasure is not working?

Tags:

java

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?

like image 778
YongHoon Avatar asked Aug 10 '26 10:08

YongHoon


2 Answers

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:

  1. The toArray method is indeed type-erased, and so the String[] attributes = introduces an implicit cast (as always happens with type erasure).
  2. However, when the call site infers at compile-time that this particular invocation has T being a String, it creates the array as a String[] — which is a subtype of Object[].
  3. It then passes that to toArray, which treats it only as an Object[] and returns it back.
  4. Back at the call site, the type erasure forces that implicit cast to 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
like image 98
yshavit Avatar answered Aug 12 '26 01:08

yshavit


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.

like image 27
Marcono1234 Avatar answered Aug 12 '26 01:08

Marcono1234



Donate For Us

If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!