我有1个界面,1个抽象类和1个具体类。注释@Handler
用于接口和抽象类中的两个方法:
public interface A<T> {
@Handler
void a(T data);
}
public abstract class B<V> implements A<Integer> {
@Override
void a(Integer data) {
// implementation
}
@Handler
public abstract void b(V data);
}
public class C extends B<String> {
@Override
void b(String data) {
// implementation
}
}
我希望从具体类@Handler
中检索用C
修饰的所有具体方法(包括超类中的那些)及其参数,即
B.a(java.lang.Integer)
C.b(java.lang.String)
我尝试使用Apache Commons Lang的MethodUtils.getMethodsListWithAnnotation()
,但它只能找到抽象方法(实际放置注释的地方),没有参数&#39;信息:
A.a(java.lang.Object)
B.b(java.lang.Object)
是否可以从抽象方法中检索具体方法及其参数类型?
答案 0 :(得分:3)
您的问题不是abstract
与具体方法有关。你有一些注释方法,其擦除不同于最重要的方法的擦除。如果它只是用于调用方法,那么您可以简单地调用描述抽象方法的Method
并确保具体类将具有针对它的具体覆盖实现,尽管它可能是委托给实际的桥接方法具有更具体擦除的实现方法。
换句话说,您的实际问题是在泛型声明的上下文中获取方法的实际类型。
这不是一件容易的事。基本上,如果方法引用了类型参数,则必须将它们替换为实际具体类的实际类型。没有保证这将起作用,即实际的类不必是可再生类型,就像ArrayList
是实现List
的具体类,但不是可再生类,所以这是不可能的在运行时通过Reflection获取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。
答案 1 :(得分:0)
当然,一旦获得short
句柄,您可以使用Method
检查该方法是否是抽象的。
如果你想要检索扩展给定类的类(事先不知道它们),这完全是另一回事 - 你可能想看看这里:How do you find all subclasses of a given class in Java?来帮助你做到这一点。获得这些类后,您可以通过标准方法(Modifier.isAbstract(method.getModifiers())
)检查它们是否覆盖相关方法以及覆盖是否具体。