java泛型反射调用方法体内类型引用有关问题
java泛型反射调用方法体内类型引用问题
自己在写自动化测试工具有关thrift服务自动化测试遇到的问题
首先给大家看一段代码
public static void test(List<Long> list){ System.out.println(list.get(0)); System.out.println(list.get(0) + 123); System.out.println(list.get(0).getClass()); } public static void bugTest1(){ String json = "[50004172,926513792]"; for(Method m :ThriftGenUtils.class.getMethods()){ if("test".equals(m.getName())){ for(Class<?> pClass : m.getParameterTypes()){ Object argCasted = Json.strToObj(json, pClass); try { m.invoke(new ThriftGenUtils(),argCasted); } catch (IllegalAccessException e) { e.printStackTrace(); } catch (InvocationTargetException e) { e.printStackTrace(); } } } } }
运行bugTest1
问题:
1 argCasted具体类型为List,其中的泛型具体类型为什么类型?为什么?
2 invoke是否能成功?
3 若invoke成功了,list的泛型具体类型是什么类型?test是否可以成功运行?若不能成功运行,到哪一行出错?报什么错误?为什么?
答案:
1 Integer,m.getParameterTypes()获取到的泛型类型是不包括具体类型的,也就是Json.strToOb调用的时候pClass只是传了List,Json转换的时候只知道是数据类型,默认转成Integer,浮点数转为Double。
2 是
3 Integer,不能成功,到+123那一行报类型转换异常Integer不能转换为Long类型,原因是泛型类型擦除。(泛型类型擦除:http://blog.****.net/caihaijiang/article/details/6403349 )
目前的解决方案为获取到泛型类型及具体类型,通过ObjectMapper.getTypeFactory().constructParametricType(clazz,genericClazzes)来获取javaType再来转换成Object对象
改造后,实现的相关代码如下
Method method = null; List<Object> argList = new ArrayList<>(); for (Method m : clientClazz.getMethods()){ if(!methodName.equals(m.getName())){continue;} method = m; Type[] types = m.getGenericParameterTypes(); Class[] classes = m.getParameterTypes(); int i = 0; for ( Type c: types) { int index = i++; String arg = args[index]; if(!(c instanceof ParameterizedType)){ String jsonStr = Json.ObjToStr(arg); argList.add(Json.strToObj(jsonStr,classes[index])); continue; } ParameterizedType pt = (ParameterizedType) c; ObjectMapper mapper = new ObjectMapper(); Class<?>[] genericClazzes = new Class<?>[pt.getActualTypeArguments().length]; int j = 0; for(Type type : pt.getActualTypeArguments()){ genericClazzes[j ++] = (Class) type; } JavaType javaType = mapper.getTypeFactory().constructParametricType(classes[index],genericClazzes); Object argCasted = mapper.readValue(arg,javaType); argList.add(argCasted); } break; } if ( method != null){ res = method.invoke(obj,argList.toArray()); }