我有以下方法使用泛型来执行它收到的列表中每个项目的getter:
public static <T, S> List<S> getValues(List<T> list, String fieldName) {
List<S> ret = new ArrayList<S>();
String methodName = "get" + fieldName.substring(0, 1).toUpperCase()
+ fieldName.substring(1, fieldName.length());
try {
if (list != null && !list.isEmpty()) {
for (T t : list) {
ret.add((S) t.getClass().getMethod(methodName).invoke(t));
}
}
} catch (IllegalArgumentException e) {
} catch (SecurityException e) {
} catch (IllegalAccessException e) {
} catch (InvocationTargetException e) {
} catch (NoSuchMethodException e) {
}
return ret;
}
如果我这样称呼它完全正常:
List<Integer> ids = getValues(List<MyDTO>, "id");
request.setListIds(ids);
但是,如果我在一行中执行它会给我一个编译错误:
request.setListIds(getValues(List<MyDTO>, "id"));
错误说:
类型中的方法setListIds(List-Integer-) MyDTO不适用于论点 (列表对象 - )
因此,当我尝试直接设置列表时,它将泛型转换为Object而不是Integer。那是为什么?
答案 0 :(得分:8)
这是由于Java非常弱的类型推断。它可以在您直接分配给变量时推断出类型,但它不会通过目标参数类型进行推断,这在第二个示例中是您需要的。
您可以使用this.<Integer>getValues...
答案 1 :(得分:1)
显然编译器没有正确猜测泛型类型变量。在分配中,他正确猜测S=Integer
,而在将结果作为参数传递时,它没有考虑方法参数的泛型类型。
这是因为类型擦除,因为方法在运行时的签名是setListIds(List)
,而不是setListIds(List<Integer>)
。顺便问一下同样的问题here,答案解释了为什么编译器的行为就像那样。
答案 2 :(得分:1)
由于type erasure。
,在方法的实际编译字节码中没有发生强制转换生成字节码时,编译器会将参数类型的任何变量视为与该参数类型的上限具有相同的类型,如果类型为无界,则将Object
视为S
。因此,如果方法中的<S extends Integer>
具有约束Integer
,则编译器会将转换插入S
。但是,由于S
是无界的,所以对Object
的任何引用都在字节码中被视为类型YourClass.<MyDTO, Integer>getValues(list, "id")
- 因此,没有强制转换。
使用您编写的方法,您可以通过在调用方法时填写类型参数来消除编译错误:
T
虽然这看起来很笨重,但你可能最好摆脱类型参数{{1}}。
答案 3 :(得分:0)
更改您的方法签名,如:
public static <T, S> List<S> getValues(List<T> list, String fieldName, Class<S> fieldType) {
//Your code goes here
}
然后下面的代码将无缝地工作:
request.setListIds(getValues(List<MyDTO>, "id", Integer.class));