6

我有以下泛型方法,它执行它接收到的列表中每个项目的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"));

错误说:

MyDTO 类型中的方法 setListIds(List-Integer-) 不适用于参数 (List-Object-)

因此,当我尝试直接设置列表时,它将泛型转换为 Object 而不是 Integer。这是为什么?

4

4 回答 4

8

这是由于 Java 的类型推断非常弱。当您直接分配给变量时,它可以推断类型,但它不会根据您在第二个示例中需要的目标参数类型进行推断。

你可以用this.<Integer>getValues...

于 2013-11-12T11:23:25.133 回答
1

显然编译器没有正确猜测泛型类型变量。在赋值中,他猜S=Integer对了,而将结果作为参数传递时,并没有考虑到方法参数的泛型类型。

这是因为类型擦除,因为该方法在运行时的签名是setListIds(List), not setListIds(List<Integer>)。顺便问一下,这里问了同样的问题,答案解释了为什么编译器会这样。

于 2013-11-12T11:24:58.793 回答
1

由于类型擦除,该方法的实际编译字节码中没有发生强制转换。

生成字节码时,编译器将参数类型的任何变量视为具有与该参数类型的上限相同的类型,或者Object该类型是无界的。因此,如果S在您的方法中有约束<S extends Integer>,编译器将插入一个强制转换为Integer. 然而,正如S无限的,任何对字节码的引用S都被视为类型Object- 因此,没有强制转换。

使用您编写的方法,您可以通过在调用方法时填写类型参数来消除编译错误:

YourClass.<MyDTO, Integer>getValues(list, "id")

虽然这看起来很笨重,但您最好摆脱 type parameter T

于 2013-11-12T11:25:49.227 回答
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));
于 2013-11-12T11:36:15.830 回答