在Java中,强制转换由泛型实例类型的getClass返回的类始终安全吗?

问题描述

我不确定如何以简洁的方式用措词表达这个问题的标题。我发现了一些相关的问题,例如this one,但似乎都没有一个人明确回答我的问题。

但实际上我要问的是:

考虑以下代码


static <A,B> Class<? extends A> getLeftClass(Pair<A,B> tuple) {
   A left = tuple.getLeft();
   return left.getClass();
}

按原样,此代码无法编译。编译失败并显示错误 Type mismatch: cannot convert from Class<capture#20-of ? extends Object> to Class<? extends A>

我认为这主要是因为getClass返回类型Class<? extends Object>,但是编译器期望Class<? extends A>

一种解决方案是按以下方式强制转换该类:


static <A,B> tuple) {
   A left = tuple.getLeft();
   return (Class<? extends A>) left.getClass();
}

编译。但是,由于未选中强制转换,因此会有警告。

我的问题是,警告是否合理?有正当的理由不这样做吗?还是仅在这种情况下,编译器无法验证它是否正确,但是只要tuple.getLeft()确实是A的实例,它就始终可以工作?


顺便说一句,Pair类是this one,来自Apache commons库

解决方法

警告是合理的,因为它又允许进行其他不会引起警告的不安全操作。 Class的类型参数允许您执行动态运行时强制转换和实例化,而这些操作的类型安全性取决于类型参数的有效性。

换句话说,您的方法允许以下操作:

Pair<List<String>,?> p = Pair.of(new ArrayList<>(),null);

List<Integer> listOfI = new ArrayList<>(Arrays.asList(1,2,3));
List<String> listOfS = getLeftClass(p).cast(listOfI);
listOfS.set(1,"foo");

这种情况称为堆污染,并且Java的泛型系统保证了这种情况不会出现带有免费警告的源代码。您会得到警告并有风险。

同样,我们可以做到:

List<String> stringList = getLeftClass(p)
    .getConstructor(Collection.class).newInstance(listOfI);
assert stringList.get(0) instanceof String;// will fail

也有类似的第三方库,例如用于XML或JSON的反序列化器,在将Class对象作为参数来描述假定的返回类型(或结果的组成部分)时,对类型安全性也有类似的假设。

相关问答

错误1:Request method ‘DELETE‘ not supported 错误还原:...
错误1:启动docker镜像时报错:Error response from daemon:...
错误1:private field ‘xxx‘ is never assigned 按Alt...
报错如下,通过源不能下载,最后警告pip需升级版本 Requirem...