Why does Java claim there's 2 declared methods when bounded generics are involved?
Asked Answered
M

1

7

With the following definitions:

public interface BaseService<T, ID> {

    T findOne(ID id);

}

public class BaseServiceImpl<T,ID extends Serializable> implements BaseService<T, ID> {

    @Override
    public T findOne(ID id) {
        return null;
    }

}

Why does BaseServiceImpl.class.getDeclaredMethods() return 2 methods:

  • public java.lang.Object BaseServiceImpl.findOne(java.io.Serializable)
  • public java.lang.Object BaseServiceImpl.findOne(java.lang.Object)

Is there a way to filter these out?

Meryl answered 22/5, 2017 at 11:20 Comment(3)
Can you check whether the Object signature is marked as a bridge method?Turnkey
@chrylis Yes, indeed it is! That solves my immediate issue. Thanks a bunch! If you care to expand it a bit into an answer, I'd be happy to upvote/accept it.Meryl
I don't understand the bridge rules well enough to write a proper answer, I just noticed that it fits the pattern I've gathered.Turnkey
W
5

This is a consequence of type erasure. On the byte code level, generic signatures are only an additional attribute of the methods, not used for the JVM’s method dispatch. The actual byte code level signature is derived from the first type of the type variable’s bound, e.g. for a type variable T extends Number&Serializable, the raw signature’s substitute for T would be Number.

For your declaration,

public interface BaseService<T, ID> {
    T findOne(ID id);
}

T and ID are substituted with Object; the method’s erased signature is Object findOne(Object).

For the subtype declaration

public class BaseServiceImpl<T,ID extends Serializable> implements BaseService<T, ID> {
    @Override
    public T findOne(ID id) {
        return null;
    }
}

the erased type of ID extends Serializable is Serializable, which causes the implementation method to have the erased signature Object findOne(Serializable).

To ensure that code using the interface BaseService, calling the method Object findOne(Object), will find the implementation method, the compiler generates a bridge method having the signature Object findOne(Object) and consisting of a plain delegation to Object findOne(Serializable), performing type casts where necessary.

You can identify the bridge method by calling isBridge() on the Method instance.

You can also use the knowledge of how the type erasure works to influence the result. By changing the declaration to

public class BaseServiceImpl<T, ID extends Object&Serializable>
      implements BaseService<T, ID> {
    @Override
    public T findOne(ID id) {
        return null;
    }
}

there is no semantic difference regarding the generic type system, but the erasure of ID extends Object&Serializable will be Object, hence, the resulting erasure of the findOne method will be identical to the erasure of the interface method, therefore, no bridge method will be needed in this case.

Wanda answered 22/5, 2017 at 18:32 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.