您的问题不在于 abstract 与具体方法。您已注释方法的擦除与覆盖方法的擦除不同。如果只是为了调用方法,您可以简单地调用描述抽象方法的Method,并确保具体类将具有具体的覆盖实现,尽管它可能是委托给实际实现方法的桥接方法更具体的擦除。
换句话说,您的实际问题是关于在泛型声明的上下文中获取方法的实际类型。
这不是一件容易的事。基本上,如果方法引用了类型参数,则必须用实际具体类的实际类型替换它们。不能保证这会起作用,即实际的类不必是可具体化的类型,就像 ArrayList 是实现 List 的具体类,但不是可具体化的类,所以不可能得到实际的ArrayList 在运行时通过反射的元素类型。但是你仍然可以得到这样一个类的最具体的实现方法。
public static Set<Method> getAnnotatedMethods(
Class<?> actualClass, Class<? extends Annotation> a) {
Set<Method> raw = getRawAnnotatedMethods(actualClass, a);
if(raw.isEmpty()) return raw;
Set<Method> resolved = new HashSet<>();
for(Method m: raw) {
if(m.getDeclaringClass()==actualClass) resolved.add(m);
else {
Method x = getMoreSpecific(actualClass, m);
resolved.add(!x.isBridge()? x: resolveGeneric(x, m));
}
}
return resolved;
}
private static Method resolveGeneric(Method x, Method m) {
final Class<?> decl = m.getDeclaringClass();
Map<Type,Type> translate = new HashMap<>();
up: for(Class<?> c=x.getDeclaringClass(); c!=decl; ) {
if(decl.isInterface()) {
for(Type t: c.getGenericInterfaces()) {
Class<?> e = erased(t);
if(updateMap(decl, e, t, translate)) continue;
c = e;
continue up;
}
}
Type t = c.getGenericSuperclass();
c = erased(t);
updateMap(decl, c, t, translate);
}
Class<?>[] raw = m.getParameterTypes();
Type[] generic = m.getGenericParameterTypes();
for(int ix = 0; ix<raw.length; ix++)
raw[ix] = erased(translate.getOrDefault(generic[ix], raw[ix]));
return getMoreSpecific(x.getDeclaringClass(), x, raw);
}
private static Method getMoreSpecific(Class<?> actual, Method inherited) {
return getMoreSpecific(actual, inherited, inherited.getParameterTypes());
}
private static Method getMoreSpecific(
Class<?> actual, Method inherited, Class<?>[] pTypes) {
try {
final String name = inherited.getName();
if(inherited.getDeclaringClass().isInterface()
|| Modifier.isPublic(inherited.getModifiers())) {
return actual.getMethod(name, pTypes);
}
for(;;) try {
return actual.getDeclaredMethod(name, pTypes);
}
catch(NoSuchMethodException ex) {
actual = actual.getSuperclass();
if(actual == null) throw ex;
}
}
catch(NoSuchMethodException ex) {
throw new IllegalStateException(ex);
}
}
private static boolean updateMap(Class<?> decl, Class<?> e, Type t, Map<Type, Type> m) {
if (!decl.isAssignableFrom(e)) {
return true;
}
if(t!=e) {
TypeVariable<?>[] tp = e.getTypeParameters();
if(tp.length>0) {
Type[] arg = ((ParameterizedType)t).getActualTypeArguments();
for(int ix=0; ix<arg.length; ix++)
m.put(tp[ix], erased(m.getOrDefault(arg[ix], arg[ix])));
}
}
return false;
}
private static Class<?> erased(Type t) {
if(t instanceof Class<?>) return (Class<?>)t;
if(t instanceof ParameterizedType)
return (Class<?>)((ParameterizedType)t).getRawType();
if(t instanceof TypeVariable<?>) return erased(((TypeVariable<?>)t).getBounds()[0]);
if(t instanceof GenericArrayType) return Array.newInstance(
erased(((GenericArrayType)t).getGenericComponentType()), 0).getClass();
return erased(((WildcardType)t).getUpperBounds()[0]);
}
/**
* You may replace this with the MethodUtils.getMethodsListWithAnnotation()
* you were already using.
*/
private static Set<Method> getRawAnnotatedMethods(
Class<?> c, Class<? extends Annotation> a) {
Set<Method> s = new HashSet<>();
for(; c!=null; c=c.getSuperclass()) {
for(Method m: c.getDeclaredMethods()) {
if(m.isAnnotationPresent(a)) s.add(m);
}
for(Class<?> ifC: c.getInterfaces()) {
for(Method m: ifC.getMethods()) {
if(m.isAnnotationPresent(a)) s.add(m);
}
}
}
return s;
}
此方法所做的第一件事是查找具有相同原始参数类型和最具体声明类的方法。如果方法由接口声明或已声明为public,则实现/覆盖方法也必须为public,因此一个简单的getMethod 就足够了。否则,使用getDeclaredMethod 遍历层次结构是不可避免的。
然后,一个简单的检查告诉我们是否需要做更多。如果存在具有更具体参数和/或返回类型的方法,那么我们刚刚找到的方法必须是桥接方法。如果是这样,我们必须遵循可能是通用的extends 或implements 关系链来获取类型参数的实际类型。然后,我们必须将方法对这些类型参数的使用替换为实际类型。如果不可能,例如如果方法本身是泛型的或实际类型不可具体化,则所有剩余的类型参数都将替换为擦除其边界。
最后,查找具有该擦除的方法。如您所见,这是一个复杂的过程,所以我不能保证这段代码可以处理所有极端情况。但它有能力of handling your use case。