强制转换由通用实例类型的 getClass() 返回的类在 Java 中总是安全的吗?

Wil*_*ver 6 java generics casting type-erasure

我不知道如何用简洁的方式来表达这个问题的标题。我发现了一些相关的问题,例如这个,但似乎没有一个能明确回答我提出的问题。

但基本上我要问的是:

考虑以下代码


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

按原样,此代码无法编译。编译失败并出现错误 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> Class<? extends A> getLeftClass(Pair<A, B> tuple) {
   A left = tuple.getLeft();
   return (Class<? extends A>) left.getClass();
}
Run Code Online (Sandbox Code Playgroud)

这编译。但是,由于未经检查的强制转换,会出现警告。

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


顺便说一下,这个Pair类是这个,来自 Apache 公共库

Hol*_*ger 3

该警告是合理的,因为它反过来允许其他不会引起警告的不安全操作。的类型参数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");
Run Code Online (Sandbox Code Playgroud)

这种情况称为堆污染,Java 的泛型类型系统保证在无警告的源代码中不会发生这种情况。您会收到警告并面临风险。

同样,我们可以这样做:

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

有类似的第 3 方库,例如 XML 或 JSON 的反序列化器,在提供对象Class作为参数来描述假定的返回类型(或结果的组件)时,对类型安全具有类似的假设。

  • @Eugene我遇到过这样的场景,我自己需要对“Class”对象进行这种未经检查的强制转换;重要的一点是将对象的转换和后续使用保持在同一代码单元中,尽可能接近,以便保持可以识别的原因和含义。隐藏演员表的实用方法会适得其反。 (2认同)