我遇到了将List类型从反射对象转换为该对象的set方法的问题。
我不确定问题是在前端还是在Gson解析端,但我的测试代码在这里:
public static void main(String[] args) throws Exception{
String json = "[52881]";
ListObject lo = new ListObject();
for(Field f : lo.getClass().getDeclaredFields())
if(List.class.isAssignableFrom(f.getType())){
Method m = lo.getClass().getMethod(
new StringBuilder("set").append(f.getName().substring(0,1).toUpperCase()).append(f.getName().substring(1,f.getName().length())).toString(),
f.getType()
);
System.out.println(((ParameterizedType) f.getGenericType()).getActualTypeArguments()[0]);
getListFromJson(m, lo, json,
((ParameterizedType) f.getGenericType()).getActualTypeArguments()[0].getClass());
}
ListObject p = (ListObject) lo;
System.out.println(gson.toJson(p.getAList()));
for(long pid : p.getAList()){
System.out.println(pid);
}
}
@SuppressWarnings("unchecked")
public static <T> void getListFromJson(Method m, Object target, String json, Class<T> elementType) throws Exception {
m.invoke(target, (List<T>)gson.fromJson(json, new TypeToken<List<T>>(){}.getType()));
}
ListObject:
public class ListObject {
List<Long> aList = new ArrayList<Long>();
public List<Long> getAList() {
return aList;
}
public void setAList(List<Long> aList) {
this.aList = aList;
}
}
此测试程序产生以下输出:
class java.lang.Long
[52881.0]
线程中的异常&#34; main&#34; java.lang.ClassCastException:java.lang.Double无法强制转换为java.lang.Long 在test.GenericTester.main(GenericTester.java:66)
第66行是
for(long pid : p.getAList()){
我在这里使用这个答案来获取getList方法: https://stackoverflow.com/a/18321048/2555197
答案 0 :(得分:3)
这是因为Java泛型的实现方式:<T>
只能存在于编译器的脑海中或类文件元数据中。让我们仔细看看并进行简要分析:
@SuppressWarnings("unchecked")
public static <T> void getListFromJson(Method m, Object target, String json, Class<T> elementType) throws Exception {
m.invoke(target, (List<T>)gson.fromJson(json, new TypeToken<List<T>>(){}.getType()));
}
Class<T> elementType
根本不玩这里。这只是尝试对该方法设置一些通用限制,但是如果你删除了参数,那么什么都不会改变。此外,Class<T>
只能保留原始类型,即使您使用未经检查的强制转换来使Class<T>
保持List<List<List<String>>>
之类的内容,您仍然会获得最顶层的原始类型{{1你可以删除参数(但是,请按照下面描述的方案保留它)。List
或new TypeToken<String>
- 在这种情况下,编译器可以基于已知类型生成正确的超级参数化。这些类型用于类型令牌。 new TypeToken<List<Integer>>
在编译时是 unknown 。在运行时检查它:<T>
- 输出为System.out.println(new TypeToken<T>() {}.getType());
。什么是T
?只是一个通配符,而不是真正的类型。T
(非法而非List<Integer>.type
!)的内容,它们只是Java支持的便捷方式。类型标记只是分析它们的参数化是可能是.class
个实例的返回Type
个实例。此外,你可以创建你自己的ParameterizedType
没有类型标记,因为这些只是定义一些getter的接口。因此,为了动态构建类型标记:
ParameterizedType
; TypeToken.getParameterized(List.class, elementType).getType()
实例:ParameterizedType
非常自我描述,不是吗?
new ParameterizedType() {
@Override public Type getRawType() { return List.class; }
@Override public Type[] getActualTypeArguments() { return new Type[]{ elementType }; }
@Override public Type getOwnerType() { return null; }
}
而不是Double
?拥有Long
的通配符,Gson使用默认策略,就好像该类型是完全未知的一样:默认情况下,所有数字都使用双精度数,因为它们可以保留所有标准Java数值。这就是为什么只在取消引用列表元素时获得强制转换异常的原因(但不是列表 - 局部变量不存在类型参数。)很抱歉这么模糊的解释,但总结一下,你只需要以下方法:
<T>
注意,现在static <T> void getListFromJson(final Method method, final Object target, final String json, final Type elementType)
throws InvocationTargetException, IllegalAccessException {
final List<T> ts = gson.fromJson(json, TypeToken.getParameterized(List.class, elementType).getType());
method.invoke(target, ts);
}
参数在游戏中玩,类型标记用于创建elementType
实例,Gson现在可以使用反序列化策略用于ParameterizedType
定义的类型( elementType
在那个地方)。
输出:
class java.lang.Long
[52881]
52881