2

I was wondering why the following code cannot use the compile-time generic type information to correctly lookup the most specific method overload, instead it always uses the method that is applicable to all potential generic parameters. Is there no way of switching on a generic parameter type at compile time to avoid nasty reflection at runtime?

import org.junit.Assert;
import org.junit.Test;

import java.util.ArrayList;
import java.util.List;

public class Temp {
    class A<T> {
        T x;
        A(T x) {
            this.x = x;
        }
        String bar = foo(x);

        private String foo(Integer i) {
            return "Int";
        }
        private String foo(String i) {
            return "String";
        }
        private <T> String foo(List<T> l) {
            return "List";
        }
        private <T> String foo(T v) {
            return "Value";
        }
    }

    @Test
    public void IntTest() {
        Assert.assertEquals(new A<Integer>(1).bar, "Int");
    }
    @Test
    public void StringTest() {
        Assert.assertEquals(new A<String>("A").bar, "String");
    }
    @Test
    public void ListTest() {
        Assert.assertEquals(new A<List<String>>(new ArrayList<String>()).bar, "List");
    }
    @Test
    public void LongTest() {
        Assert.assertEquals(new A<Long>(1L).bar, "Value");
    }
}
Woodz
  • 1,029
  • 10
  • 24
  • possible duplicate of [Method has the same erasure as another method in type](http://stackoverflow.com/questions/1998544/method-has-the-same-erasure-as-another-method-in-type) – Duncan Jones Apr 18 '13 at 12:35
  • The types do not have the same erasure though - Integer, String, List are all different erasures – Woodz Apr 18 '13 at 13:07

2 Answers2

4

This is because the compiler would have to generate a different class A for each type that is used for T (because the initializer for A.bar would need to call a different method). The decision was made to generate only one class. This is different from e.g. C++ where the compiler generates a new type for all used type parameters.

In other words the compiled code for type A<String> would be different from type A<Integer>, but after compilation there is only type A.

herman
  • 11,740
  • 5
  • 47
  • 58
3

This is beacuse generics are removed at compile time. There is concept of erasure which removes all generics code at compile time replaces it with type cast. So java will not be aware of generics methods at run time.

EDIT:

Check this link also: http://docs.oracle.com/javase/tutorial/java/generics/restrictions.html#cannotOverload

Lokesh
  • 7,810
  • 6
  • 48
  • 78
  • 1
    Question is why can't it be done at compile time, so the information being removed at the same time doesn't make sense as an explanation. It's because there Java generics are not a templating mechanism. There is only one call to `foo` within the example code. Not selecting override based on runtime type was implemented pre-erasure. – Tom Hawtin - tackline Apr 18 '13 at 12:49
  • So to clarify, this is because generics are removed at compile time BEFORE method overload resolution is chosen? – Woodz Apr 18 '13 at 13:06
  • @Woodz The accepted answer for the [question I marked as a duplicate](http://stackoverflow.com/questions/1998544/method-has-the-same-erasure-as-another-method-in-type) explains this very clearly. – Duncan Jones Apr 18 '13 at 13:09