简体   繁体   中英

How is the generic java erasure affecting the usage of newInstance()?

Based on the java documentation:

During the type erasure process, the Java compiler erases all type parameters and replaces each with its first bound if the type parameter is bounded, or Object if the type parameter is unbounded.

Now, the thing is, with a class like that:

public class MyCreator<T> { 
    Class<T> kind; 

    MyCreator(Class<T> kind) {
        this.kind = kind; 
    }

    void printInstanceClass() throws Exception{ 
        System.out.println(kind.newInstance().getClass());
    }

    public static void main(String[] args) throws Exception{ 
        MyCreator<String> myCreator = new MyCreator<String>(String.class);

        myCreator.printInstanceClass();
    } 
}

the printInstanceClass() method is printing actually "class java.lang.String".

If the compiler is replacing T inside of the class with Object, I guess that also the information on Class associated with kind is removed and replaced with Class. So, how can the newInstance() method return a String instance instead of an Object one?

You pass String.class (which is NOT erased, but a concrete class retained at runtime), then call newInstance on that class, which yields a String.

The output examines the concrete object (String) and displays its class.

The tricky part is the String.class parameter . It's not a type parameter , but a plain old param, like String[] args . So, the Class<T> kind will be String.class thus the printInstanceClass() can access it at runtime.

It's a common practice, suggested in Effective Java too.

So after type erasure this code is logically equivalent to this one:

public class MyCreator { 
    Class kind; // this will be String.class at runtime

    MyCreator(Class kind) {
        this.kind = kind; 
    }

    void printInstanceClass() throws Exception{ 
        System.out.println(kind.newInstance().getClass());
    }

    public static void main(String[] args) throws Exception{ 
        MyCreator myCreator = new MyCreator(String.class);

        myCreator.printInstanceClass();
    } 
}

Information about the class (type) you passed as generic argument of your myCreator object is erased, but not information stored inside the kind object.

Using Class<?> object methods is fine, but if you tried to obtain class information from generic T type you'd encounter a compilation error, for instance:

void printInstanceClass() throws Exception{ 
    System.out.println(T.class.newInstance().getClass()); // error!
}

Although it's not easy, workarounds for doing the above exist too.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM