views:

316

answers:

3

Hi, As per my understanding the following generic function in java:

public static <T> T f(T x) {
   Integer[] arr = new Integer[4];
   T ret = (T) arr[2];
   return ret;
}

is compiled to the following form (as it is unbounded):

public static Object f(Object x) {
   Integer[] arr = new Integer[4];
   Object ret = (Object) arr[2];
   return ret;
}

However, when I run the following statement, the compiler is able to figure out the return value to be Integer type. How does the compiler figure it out?

Integer i = f(new Integer(4));

Shouldn't the function be written as following for the above statement to work?

  public static <T extends Integer> T f(T x) {
       Integer[] arr = new Integer[4];
       T ret = (T) arr[2];
       return ret;
    }
+5  A: 

Somebody with more knowledge in this topic might jump in later, meanwhile my humble explanation is this:

What you say is right, generics have type erasure which means that generic information is not available at runtime. It is however available at compile time and that's how the compiler can figure out that you're mixing types.

Hope that helps!

Pablo Fernandez
+1  A: 

In your example, the compiler figures that the return type is the same type as the parameter passed to the function, since they're both parameterized to type T, which is resolved to be Integer. When generating the bytecode, it then erases the type parameters information.

Jordão
Thats interesting. But wouldn't the JVM complain at runtime since the object returned is of type Object?
Why would it complain about that? An Integer *is* an Object, they're both internally implemented as a pointer to a chunk of memory. It's basically the same as casting it, except the JVM doesn't bother type-checking because it knows the cast is safe.
Anon.
+3  A: 

Generics use type erasure. That basically means that generics are nothing more than implicit casts so when you do:

List<Integer> ...

it's no different to a normal List and may contain Integers or anything really. You're just telling Java to cast get() to an Integer (and other things). The type simply isn't retained at runtime (mostly).

Arrays are different. Arrays are what's called covariant. That means their type is retained at runtime. So you can do:

List<Integer> list1 = new ArrayList<Integer>();
list2 = (List<String>)list1;
list2.add("hello");

which is perfectly legal and will compile and run. But:

Integer[] arr1 = new Integer[10];
String[] arr2 = (String[])arr1; // compiler error

But it gets more subtle than that too.

Integer[] arr1 = new Integer[10];
Object[] arr2 = (Object[])arr1;
arr2[5] = "hello"; // runtime error!

As to your function. When you write:

public static <T> T f(T x) {
  Integer[] arr = new Integer[4];
  T ret = (T) arr[2];
  return ret;
}

you're telling the compiler to derive T, being the argument type and the return type, from the argument. So when you pass in an Integer the return type is Integer. When you call:

Integer i = f(new Integer(4));

the compiler is simply following your instructions. The function does take and return an Object in compiled form but it just does this:

Integer i = (Integer)f(new Integer(4));

implicitly.

Just like the List example above there is nothing stopping you have f() return anything you like rather than what it's supposed to return based on the parameterized type.

cletus
I don't think 'covariant' means that their types are retained at runtime... don't covariant and contravariant have something to do with inheritance and if a subclass can take the place of a superclass?
Pablo Fernandez
@Pablo: see http://www.developertutorials.com/tutorials/java/java-theory-generic-050323/page2.html
cletus
@Cletus funny. In the first paragraph of that blogpost says: "Arrays in the Java language are covariant -- which means that if `Integer` extends `Number` (which it does), then not only is an `Integer` also a `Number`, but an `Interger[]` is also a `Number[]`"
Pablo Fernandez
@cletus - Pablo Fernandez is correct, covariance has nothing to do with whether a type is retained at runtime. The word you are looking for is "reified".
danben