根据java编译器规则在Class中搜索匹配指定参数类型表的泛型方法(GenericMethod)

因为项目的需要,设计了一个满足特定需要的代码自动生成工具。在开发过程中需要根据方法名和方法参数类型数组在指定的类中根据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 <T>void test(int a,URL b,T c){
		
	}

	public <T1,T2,T3>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<java.lang.reflect.Method> methods=new ArrayList<java.lang.reflect.Method>();
		//查找同名且参数数目相同的所有方法
		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<java.lang.reflect.Method> 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<java.lang.reflect.Method> 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<java.lang.reflect.Method> 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<?>, Class<?>> PRIMITIVE_TYPE_MAP = new HashMap<Class<?>, 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);
		}
	};

补充说明
细心严谨的读者可能会发现这里面的逻辑并不十分严谨,可能会出现返回并不匹配方法的结果,不过在我的应用场景中有别的措施做了保证,所以不会有问题,你可以根据自己需要再补充一些检查代码。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

10km

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值