我正在创建一个实用程序函数,该函数返回给定所有部分的Method
对象。其参数:
- 返回类型 (
Class<?>
) - 包含类 (
Class<?>[]
) - 函数名称 (
String
) - 参数类数组 (
Class<?>[]
)
请注意,包含类参数是一个数组。这些类中只有一个必须包含该方法,但您无法知道是哪一个。除了可以显式指定的类之外,这是指定默认类的一种方式。
我的目标是按顺序搜索此数组中的每个类,如果它包含该方法,则返回其Method
对象。我想这样做而不必尝试捕捉NoSuchMethodException
(如本问题中所述)。由于该方法可能是私有的,因此无法使用Class.getMethods()
。
(它还允许私有静态,但我不确定这是否会影响这个问题。
您如何以这种方式搜索私有方法?如何进行"类中是否存在函数"测试,而不必使用NoSuchMethodException
作为"错误"逻辑?
详细信息: https://www.google.com/search?q=class+contains+method+java
使用 getDeclaredMethods
返回所有方法,包括非公共方法。
for(Class<?> cls : classesToCheck) {
for(Method m : cls.getDeclaredMethods()) {
if(!m.getName().equals(methodName))
continue;
if(m.getReturnType() != returnType)
continue;
Class<?>[] pams = m.getParameterTypes();
if(pams.length != pamsToCheckFor.length)
continue;
int i;
for(i = 0; i < pams.length; i++) {
if(pams[i] != pamsToCheckFor[i])
break;
}
if(i == pams.length)
return m; // or return true
}
}
// not found
这是相当多的工作,但肯定可以完成。
请注意,如果您经常使用相同的类执行此类操作,则可能需要将 Method 对象存储在本地某处,例如 HashMap。调用 getFields
/getMethods
/getConstructors
/etc 每次都会创建一个新数组,并复制其中的所有对象。仅仅找到一件事就是一种相对昂贵的操作。
作为琐事,有一个名为sun.reflect.ReflectionFactory
的类可以制作这些防御性副本。
java.lang.Class
的源代码还显示,像getMethod
和getDeclaredMethod
这样的方法与上面的代码片段非常相似,只是它们可能不会制作副本。因此,作为性能优化的一部分,复制是值得权衡的。如果不存在的方法被认为是不太可能的结果,除非您像我建议的那样缓存方法,否则搜索数组实际上可能不会更快。
考虑搜索类似 java.lang.String
的类。这是一个相当大的类,调用getDeclaredMethods
将创建相当多的对象。
@Radiodef的答案,但它缺少一些东西:getDeclaredClass
只返回在Class
本身中声明的方法,而不是在其超类中声明的方法。由于超类方法通常也被视为类的方法,因此您也需要搜索它们。
其次,在指定返回类型时,通常希望找到一个返回方法,该方法返回可以分配给该返回类型的内容,而不是确切的返回类型。在 Java 中,重写超类方法的方法返回超类的返回类型的子类是合法的。即超类有Number myNumber()
,那么子类可能有@Override Integer myNumber()
。
如果你也考虑到这一点,你会得到这个代码:
public static Method findMethod(Class<?> returnType, Collection<? extends Class<?>> containingClasses, String functionName, Class<?>[] parameters) {
for (Class<?> containingClass : containingClasses) {
Method m = findMethodInClass(returnType, containingClass, functionName, parameters);
if (m != null)
return m;
}
return null;
}
private static Method findMethodInClass(Class<?> returnType, Class<?> containingClass, String functionName, Class<?>[] parameters) {
for (Method m : containingClass.getDeclaredMethods()) {
if (checkMethod(m, returnType, functionName, parameters))
return m;
}
if (containingClass.getSuperclass() != null)
return findMethodInClass(returnType, containingClass.getSuperclass(), functionName, parameters);
else
return null;
}
private static boolean checkMethod(Method method, Class<?> returnType, String functionName, Class<?>[] parameters) {
if (!method.getName().equals(functionName))
return false;
// Also allow overridden classes that return a subtype of the requested type
if (!returnType.isAssignableFrom(method.getReturnType()))
return false;
Class<?>[] actualParameters = method.getParameterTypes();
if (actualParameters.length != parameters.length)
return false;
for (int i = 0; i < actualParameters.length; i++) {
if (actualParameters[i] != parameters[i])
return false;
}
return true;
}
public static void main(String[] args) {
System.out.println(findMethod(Integer.TYPE, Collections.singleton(Integer.class), "intValue", new Class<?>[0]));
System.out.println(findMethod(String.class, Collections.singleton(Random.class), "toString", new Class<?>[0]));
}
不过,我建议不要出于任何原因在sun.*
包中使用任何类;包括sun.reflect.ReflectionFactory
。这些类不是受支持的 Java 公共接口的一部分,可以随时更改或删除。
请参阅:Sun 发生了什么。