5

I have a generic java interface Id<T> with a single method T getId() and a class MyClass that implements Id<Long>. When I inspect the methods declared on MyClass using java reflection, I see two methods: one with return type Long, and one with return type Object. Where does the second method come from and how can I remove it?

Here is the source:

package mypackage;

import java.lang.reflect.Method;

public class MainClass {
    public static void main(String[] args) {
        for (Method method : MyClass.class.getDeclaredMethods()) {
            System.out.println(method);
        }
        // prints out two lines
        // public java.lang.Long mypackage.MyClass.getId()   <-- ok
        // public java.lang.Object mypackage.MyClass.getId() <-- not ok
    }
}

interface Id<T> {
    T getId();
}

class MyClass implements Id<Long> {
    @Override
    public Long getId() {
        return new Long(0);
    };
}
JCvanDamme
  • 621
  • 5
  • 20
  • In a method with Returntype `Long` you can also return a primitive `long` e.g. `return 0L;`. It will be autoboxed to a `Long`. Not a answer to your question, but maybe you didnt know. – Felix Jun 26 '17 at 13:43

3 Answers3

3

The second method is a synthetic bridge method, you can check it with method.isSynthetic() or method.isBridge(). You can't remove it, but if you don't want to see it in the list of declared methods, just check all those methods to have isBridge() == false.

Synthetic bridge methods are added automatically to generic classes during compilation. You can read more about synthetic methods here.

for (Method method : MyClass.class.getDeclaredMethods()) {
    System.out.println("Method: " + method);
    System.out.println("synthetic: " + method.isSynthetic());
    System.out.println("bridge: " + method.isBridge());
}

// 1
//Method: public java.lang.Long Main$MyClass.getId()
//synthetic: false
//bridge: false
// 2
//Method: public java.lang.Object Main$MyClass.getId()
//synthetic: true
//bridge: true
esin88
  • 3,091
  • 30
  • 35
2

This is feature. Same issue was raised in

JDK-8060179 Class.getDeclaredMethods() returning inconsistent results with generics and closed as 'not a bug'.

Bartosz Bilicki
  • 12,599
  • 13
  • 71
  • 113
1

You can find the compiled methods by javap -c MyClass:

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

  public java.lang.Long getId();
    Code:
       0: new           #2                  // class java/lang/Long
       3: dup
       4: lconst_0
       5: invokespecial #3                  // Method java/lang/Long."<init>":(J)V
       8: areturn

  public java.lang.Object getId();
    Code:
       0: aload_0
       1: invokevirtual #4                  // Method getId:()Ljava/lang/Long;
       4: areturn
}

As you can see there are 2 methods for getId, one return type is Long as implementation and the another return type is Object type (it's invoking the Long getId method).

The return type Object method is for handling when no specify the generic type and bridge to Long getId method. Example:

    Id<Long> id = new Id<Long>() {
        @Override
        public Long getId() {
            return null;
        }
    };
    Long id1 = id.getId();

as the above code snippet we can implement the Id anonymous class with the Long type. but the problem we also can implement the Id anonymous class like without specify generic type in variable:

    Id id = new Id<Long>() {
        @Override
        public Long getId() {
            return null;
        }
    };
    Object id1 = id.getId();

so for now the compiler can't infer generic type for variable id, when id.getId() it's returning type Object type variable, it means it's calling this method public java.lang.Object getId(); and bridge to public java.lang.Long getId(); method.

chengpohi
  • 14,064
  • 1
  • 24
  • 42