【问题标题】:Is casting the class returned by getClass() of a generic instance type always safe in Java?在 Java 中强制转换由通用实例类型的 getClass() 返回的类是否总是安全的?
【发布时间】:2020-10-16 15:23:34
【问题描述】:

我不知道如何简明扼要地表达这个问题的标题。我发现了一些相关的问题,例如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&lt;capture#20-of ? extends Object&gt; to Class&lt;? extends A&gt;

我认为这本质上是因为getClass 返回一个类型Class&lt;? extends Object&gt;,但编译器期待Class&lt;? extends A&gt;

一种解决方案是按如下方式转换类:


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

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

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


顺便说一下,Pair 类是 this one,来自 Apache 公共库

【问题讨论】:

  • 你的假设对我来说是正确的。我认为 x.getClass() 没有将其返回类型声明为 Class&lt;? extends X&gt; 是 Java 泛型的一个怪癖;特别是因为编译器有时会像对待它一样对待它。
  • 尝试使用返回类型为Class&lt;A&gt; 的原始代码。我认为extends 是错误的。如果有的话,类类型应该是 A 的超类,而不是子类。
  • @markspace A 类型的变量可以在运行时保存A 类型或A 的任何子类型的对象。因此Class&lt;? extends A&gt;.
  • @khelwood 我不认为泛型是这样协变的。 A 不是变量而是类型参数。它的类型只能是 A。(至少,在没有任何限制的情况下,我认为 A 不会解析为除自身之外的任何内容。)
  • @khelwood 我想,你的意思是this rule, "getClass 的方法调用表达式的类型是Class&lt;? extends |T|&gt;,其中T 是类或接口搜索getClass(§15.12.1)和|T|表示删除T(§4.6)。“擦除点可能很烦人,但正如my answer所示,删除该规则会导致在不安全的代码中。

标签: java generics casting type-erasure


【解决方案1】:

警告是有道理的,因为它反过来允许其他不会引起警告的不安全操作。 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

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

【讨论】:

  • 谢谢你证明我错了。现在的问题是getLeftClass 对 OP 来说有点没用,不是吗?
  • @Eugene 我遇到了我自己需要对Class 对象进行这种未经检查的强制转换的情况;重要的一点是将对象的转换和随后的使用保持在同一个代码单元中,尽可能接近,以便原因和含义保持可识别。隐藏演员表的实用方法会适得其反。
猜你喜欢
  • 1970-01-01
  • 2020-09-20
  • 1970-01-01
  • 1970-01-01
  • 2020-10-11
  • 1970-01-01
  • 2010-09-18
  • 1970-01-01
  • 2013-08-01
相关资源
最近更新 更多