简体   繁体   中英

Why do I get the base class methods via reflection when the subclass overrides them?

I have super class as:

class MyClass<T> {
  public void setValue(T value){
    //insert code
  }

  public T getValue(){
    return null;
  }
}

then I have aa specific derivation

class MyClassImp extends MyClass<String> {
  @Override
  public void setValue(String value){
    //insert code
  }

  @Override
  public String getValue(){
    return null;
  }
}

On reflection on MyClassImpl as:

Class clazz = MyClassImpl.class;
Method[] methods = clazz.getDeclaredMethods();

I get both superclass implementation java.lang.Object getValue() , void setValue(java.lang.Object) and java.lang.String getValue() , void setValue(java.lang.String) .

According to the Java documentation of Class.getDeclaredMethods() vis-a-viz

Returns an array of Method objects reflecting all the methods declared by the class or interface represented by this Class object. This includes public, protected, default (package) access, and private methods, but excludes inherited methods. The elements in the array returned are not sorted and are not in any particular order. This method returns an array of length 0 if the class or interface declares no methods, or if this Class object represents a primitive type, an array class, or void. The class initialization method <clinit> is not included in the returned array. If the class declares multiple public member methods with the same parameter types, they are all included in the returned array.

Why am I getting the super type implementation? Is there something I am missing?

The reason why I need this is that I reflectively invoke setValue on base class implementation, which I have added some special annotation comments and of course additional constraints.

It's because the compiled class actually does declare setValue(Object) . That method will cast to String, and then call the strongly typed method. Likewise getValue(Object) calls getValue(String) .

Basically this is required because the JVM doesn't really know about generics (at least not in a deep way) - in order to override the superclass method at the JVM level, it has to have the same signature.

Have a look at the class with javap -c MyclassImp you'll see the extra synthetic methods:

public java.lang.Object getValue();
  Code:
   0:   aload_0
   1:   invokevirtual   #3; //Method getValue:()Ljava/lang/String;
   4:   areturn

public void setValue(java.lang.Object);
  Code:
   0:   aload_0
   1:   aload_1
   2:   checkcast       #4; //class java/lang/String
   5:   invokevirtual   #5; //Method setValue:(Ljava/lang/String;)V
   8:   return

}

As Jon had said earlier, type information is lost at runtime for generics .

So whenever you use generics, the compiler puts all those "generic" inherited methods in the subclass. The same is not true with non-generic code.

I just checked: when I removed the generic related code (the <T> part) from the superclass and the reflection code gave me exactly two methods in the subclass even though it overrides them. It implies that the documentation should have been a little bit explicit for generic-related code.

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