Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Class name of type parameters in java?

Tags:

java

Assuming T as a class type parameter, why cannot I use T.class

I was writing a function that download a page and parses it according to a passed class. For parsing, I use another function whose signature is : ParseObject::parse(Class<T> classname)

<T> void downloadParse(){
  ParseObject obj;
  obj.parse(T.class); //<--- why compiler error here?? (whereas something like Integer.class is allowed)
}
like image 433
vivek.m Avatar asked Nov 07 '11 17:11

vivek.m


People also ask

What is a class type parameter in Java?

A type parameter, also known as a type variable, is an identifier that specifies a generic type name. The type parameters can be used to declare the return type and act as placeholders for the types of the arguments passed to the generic method, which are known as actual type arguments.

How do you declare a type parameter?

To declare a bounded type parameter, list the type parameter's name, followed by the extends keyword, followed by its upper bound, which in this example is Number . Note that, in this context, extends is used in a general sense to mean either "extends" (as in classes) or "implements" (as in interfaces).

How do you find the class of a generic type?

To implement generics, the Java compiler applies type erasure to: Replace all type parameters in generic types with their bounds or Object if the type parameters are unbounded. The produced bytecode, therefore, contains only ordinary classes, interfaces, and methods.

What is type name in Java?

public class TypeName extends Object. Any type in Java's type system, plus void . This class is an identifier for primitive types like int and raw reference types like String and List . It also identifies composite types like char[] and Set<Long> .


3 Answers

Java generics are implemented via type erasure. They can only be used for compile time checking. After compiliation, the object gets changed to the lowest common object. (In this case Object.class).

The compiled bytecode has no idea what T is.

If you want access to the class, you need to change the method to:

<T> void downloadParse(Class<T> cls){
  ParserObject obj;
  obj.parse(cls);
}
like image 113
Reverend Gonzo Avatar answered Oct 21 '22 20:10

Reverend Gonzo


Erasure is the villain here. From the Java tutorials:

For instance, Box is translated to type Box, which is called the raw type — a raw type is a generic class or interface name without any type arguments. This means that you can't find out what type of Object a generic class is using at runtime. The following operations are not possible:

public class MyClass<E> {
    public static void myMethod(Object item) {
        if (item instanceof E) {  //Compiler error
            ...
        }
        E item2 = new E();       //Compiler error
        E[] iArray = new E[10];  //Compiler error
        E obj = (E)new Object(); //Unchecked cast warning
    }
}

The operations shown in bold are meaningless at runtime because the compiler removes all information about the actual type argument (represented by the type parameter E) at compile time.

With Erasure, the type information is removed, and everything is just an Object:

When a generic type is instantiated, the compiler translates those types by a technique called type erasure — a process where the compiler removes all information related to type parameters and type arguments within a class or method. Type erasure enables Java applications that use generics to maintain binary compatibility with Java libraries and applications that were created before generics.

Then the compiler re-introduces class "T" as casts everywhere it is required. T doesn't "exist" inside the generic, so you can't create an object of class T. On the calls into and out of the generic, the compiler casts the Objects into "T" class.

Google "java erasure" for more information on how this works. Wikipedia provides some examples.

like image 26
Steve J Avatar answered Oct 21 '22 19:10

Steve J


As others said, that's not possible. But since it's a method without arguments and returning void, what would you expect T to be?

Something you might encounter once in a while is this:

<T> T foo(Class<T> cls) {
    Object o = ...;
    return cls.cast(o);
}

// usage - perfectly type safe
String s = foo(String.class);

Additionally, it's sometimes possible to get generic type arguments, e.g. here:

class Foo implements Iterable<String> {
    // snip
}

ParameterizedType pt = (ParameterizedType) Foo.class.getGenericInterfaces()[0];
System.out.println(pt); // prints java.lang.Iterable<java.lang.String>

Class<?> at = (Class<?>) pt.getActualTypeArguments()[0];
System.out.println(at.getName()); // prints java.lang.String

But that's another story ;)

like image 2
sfussenegger Avatar answered Oct 21 '22 18:10

sfussenegger