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?
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 forT
would beNumber
.For your declaration,
T
andID
are substituted withObject
; the method’s erased signature isObject findOne(Object)
.For the subtype declaration
the erased type of
ID extends Serializable
isSerializable
, which causes the implementation method to have the erased signatureObject findOne(Serializable)
.To ensure that code using the interface
BaseService
, calling the methodObject findOne(Object)
, will find the implementation method, the compiler generates a bridge method having the signatureObject findOne(Object)
and consisting of a plain delegation toObject findOne(Serializable)
, performing type casts where necessary.You can identify the bridge method by calling
isBridge()
on theMethod
instance.You can also use the knowledge of how the type erasure works to influence the result. By changing the declaration to
there is no semantic difference regarding the generic type system, but the erasure of
ID extends Object&Serializable
will beObject
, hence, the resulting erasure of thefindOne
method will be identical to the erasure of the interface method, therefore, no bridge method will be needed in this case.