因为项目的需要,设计了一个满足特定需要的代码自动生成工具。在开发过程中需要根据方法名和方法参数类型数组在指定的类中根据java编译器的规则找到与之最匹配的泛型方法。
例如,对下面这个类 ,调用test(1,new URL(“http://www.sohu.com“),new Date())会最终调用到哪个方法?
当然java器肯定知道,但它是用什么规则进行匹配的呢?
public class TestGenericMethod {
public void test(String a,String b,byte[] c){
}
public void test(Date a,URL b,byte[] c){
}
public void test(int a,URL b,T c){
}
public void test(T1 a,T2 b,T3 c){
}
}
于是对java关于泛型方法匹配的方式做了研究,发现java编译器在匹配泛型方法时,对参数的匹配是遵循从左到右的顺序来一个个检查的,根据这个规则写了下面的方法来实现泛型方法的精确匹配。
/** * @param clazz 要搜索的类 * @param name 方法名 * @param parameterTypes 希望匹配的参数类型数组 * @return * @throws NoSuchMethodException */
public static final java.lang.reflect.Method getGenericMethod(Class>clazz,String name,Class>... parameterTypes) throws NoSuchMethodException{
List methods=new ArrayList();
//查找同名且参数数目相同的所有方法
for (java.lang.reflect.Method m : clazz.getMethods())
if (m.getName().equals(name) && m.getParameterTypes().length == parameterTypes.length)
methods.add(m);
if (!methods.isEmpty()) {
//过滤掉所有不能匹配的方法
for (int i = 0; i < parameterTypes.length; i++) {
for (Iterator it = methods.iterator(); it.hasNext();) {
if (!isConvert(it.next().getParameterTypes()[i], parameterTypes[i]))
it.remove();
}
if (methods.size() <= 1)
//找到唯一匹配的方法或没有匹配的方法就中止循环
break;
}
if (methods.size() > 1) {
//如果还有多个方法满足条件,再过滤掉不能直接赋值的方法
for (int i = 0; i < parameterTypes.length; i++) {
for (Iterator it = methods.iterator(); it.hasNext();) {
if (!isAssignable(it.next().getParameterTypes()[i], parameterTypes[i]))
it.remove();
}
if (methods.size() <= 1)
//找到唯一匹配的方法或没有匹配的方法就中止循环
break;
}
}
if (methods.size() == 1)
return methods.iterator().next();
else if (methods.size() > 1){
//如果还有多个方法满足条件,再过滤掉类型不相等的方法
for (int i = 0; i < parameterTypes.length; i++) {
for (Iterator it = methods.iterator(); it.hasNext();) {
if (it.next().getParameterTypes()[i]!= parameterTypes[i])
it.remove();
}
if (methods.size() <= 1)
//找到唯一匹配的方法或没有匹配的方法就中止循环
break;
}
if (methods.size() == 1)
return methods.iterator().next();
else if (methods.size() > 1)
throw new IllegalStateException("found more matched method");
}
}
//没有找到匹配的方法就抛出异常
throw new NoSuchMethodException();
}
/** * from对象是否能转换成to * @param to * @param from * @return */
public static final boolean isConvert(Class> to, Class> from) {
if (!isAssignable(to, from)){
if (to.isPrimitive() && PRIMITIVE_TYPE_MAP.get(to) == from) {
return true;
}
return false;
}
return true;
}
/** * from对象能否则直接赋值给to * @param to * @param from * @return */
public static final boolean isAssignable(Class> to,Class>from){
if (from.isPrimitive()) {
if (to != from && !to.isAssignableFrom(PRIMITIVE_TYPE_MAP.get(from)))
return false;
} else if (!to.isAssignableFrom(from))
return false;
return true;
}
//primitive类型与对应Object类的映射表
private static final Map, Class>> PRIMITIVE_TYPE_MAP = new HashMap, Class>>() {
private static final long serialVersionUID = 2638066380035384674L;
{
put(int.class, Integer.class);
put(boolean.class, Boolean.class);
put(byte.class, Byte.class);
put(short.class, Short.class);
put(char.class, Character.class);
put(long.class, Long.class);
put(float.class, Float.class);
put(double.class, Double.class);
}
};
补充说明
细心严谨的读者可能会发现这里面的逻辑并不十分严谨,可能会出现返回并不匹配方法的结果,不过在我的应用场景中有别的措施做了保证,所以不会有问题,你可以根据自己需要再补充一些检查代码。