Reflection can't get the actual parameter type of a method that overrides from a generic typed interface?

EDIT: Sorry the example I provide below doesn't reproduce the problem... I'm trying to find the right way to reproduce it. Before that, you may just ignore my question...

Usually we grab the parameter type of a method like this:

Class<?> clazz = method.getParameterTypes()[0]

I have never thought twice about it before, but recently I bumped into an issue that I always got class java.lang.Object when doing that. After an inspection, I found out the case can be described like this:

/** A parameterized interface */
public interface IService<T> {

    Result create(T obj);
}

/** An implementation */
public class CarService implements IService<Car> {

    public Result create(Car car) { ... }
}

And then when I use the way above to get the class of the parameter car, it turns out to be a class java.lang.Object:

Method methods = CarService.class.getMethods();
for (Method method : methods) {
    Class<?>[] types = method.getParameterTypes();
    // when the loop meets "create" here ...
}

I guess I have totally forgot something very fundamental about type erase? But now I really need get the actual parameter type in this case, if it's possible...

Jon Skeet
people
quotationmark

Yes, this is type erasure being annoying. It's worth looking at what you get in bytecode here:

class Car {}
interface IService<T> {
    void create(T obj);
}

class CarService implements IService<Car> {
    public void create(Car car) {}
}

Then:

$ javac Test.java # Contains all the above...
$ javap -c CarService

Compiled from "Test.java"
class CarService implements IService<Car> {
  CarService();
    Code:
       0: aload_0
       1: invokespecial #1                  // Method java/lang/Object."<init>":()V
       4: return

  public void create(Car);
    Code:
       0: return

  public void create(java.lang.Object);
    Code:
       0: aload_0
       1: aload_1
       2: checkcast     #2                  // class Car
       5: invokevirtual #3                  // Method create:(LCar;)V
       8: return
}

And those are also shown when you use reflection:

public class Test {
    public static void main(String[] args) {
        Method[] methods = CarService.class.getDeclaredMethods();
        for (Method method : methods) {
            System.out.println(method.getName());
            for (Class<?> type : method.getParameterTypes()) {
                System.out.printf("- %s%n", type.getName());
            }
        }
    }
}

Output:

create
- Car
create
- java.lang.Object

How you use this will depend on exactly what you're trying to achieve, but hopefully the awareness that there are multiple methods in the bytecode will help...

people

See more on this question at Stackoverflow