Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Implementing generic java interface adds additional method [duplicate]

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);
    };
}
like image 453
JCvanDamme Avatar asked Jun 26 '17 13:06

JCvanDamme


3 Answers

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
like image 145
esin88 Avatar answered Sep 20 '22 18:09

esin88


This is feature. Same issue was raised in

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

like image 35
Bartosz Bilicki Avatar answered Sep 21 '22 18:09

Bartosz Bilicki


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.

like image 45
chengpohi Avatar answered Sep 21 '22 18:09

chengpohi