Is casting the class returned by getClass() of a generic instance type always safe in Java?

huangapple go评论145阅读模式
英文:

Is casting the class returned by getClass() of a generic instance type always safe in Java?

问题

以下是翻译好的内容:

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

但是基本上我在问的是:

考虑下面的代码


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> Class<? extends A> getLeftClass(Pair<A, B> tuple) {
   A left = tuple.getLeft();
   return (Class<? extends A>) left.getClass();
}

这段代码可以编译通过。然而,会有一个未经检查的类型转换警告。

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


顺便说一下,Pair类可以在这里找到,它来自于Apache commons库。

1: https://stackoverflow.com/questions/339699/java-generics-type-erasure-when-and-what-happens#:~:text=If%20you%20have% a%20method,are%20compiled%20into%20the%20class.&text=If%20you%20have% code%20that,parameterized%20type%20is%20%22erased%22.
2: https://commons.apache.org/proper/commons-lang/apidocs/org/apache/commons/lang3/tuple/Pair.html

英文:

I am not sure how to word the title of this question in a concise way. There are a few related questions I have found, for instance this one, but none of them seem to answer the question I have explicitly.

But essentially what I am asking is this:

Consider the following code


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

As is, this code does not compile. The compilation fails with the error
Type mismatch: cannot convert from Class&lt;capture#20-of ? extends Object&gt; to Class&lt;? extends A&gt;

I think this is essentially because getClass returns a type Class&lt;? extends Object&gt; but the compiler is expecting Class&lt;? extends A&gt;.

A solution is to cast the class as follows:


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

This compiles. However, there is a warning because of an unchecked cast.

My question is, is the warning justified? Is there a legitimate reason not to do this? Or is this just a case where the compiler can't verify that it is correct, but it will always work as long as tuple.getLeft() is indeed an instance of A?


By the way, the Pair class is this one, from the Apache commons library

答案1

得分: 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");

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

同样,我们可以这样做:

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

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

英文:

The warning is justified, as it allows in turn other unsafe operations that would not cause a warning. The type parameter of Class allows you to perform dynamic runtime casts and instantiations and the type safety of those operations depends on the validity of the type parameter.

In other words, your method allows the following operation:

Pair&lt;List&lt;String&gt;,?&gt; p = Pair.of(new ArrayList&lt;&gt;(), null);

List&lt;Integer&gt; listOfI = new ArrayList&lt;&gt;(Arrays.asList(1, 2, 3));
List&lt;String&gt; listOfS = getLeftClass(p).cast(listOfI);
listOfS.set(1, &quot;foo&quot;);

This situation is called heap pollution and Java's generic type system guarantees that this situation can not occur with warning free source code. You get a warning and have the risk.

Likewise, we could do:

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

There are similar 3rd party libraries, e.g. deserializers for XML or JSON, with similar assumptions about the type safety when being provided with a Class object as parameter to describe the assumed return type (or a component of the result).

huangapple
  • 本文由 发表于 2020年10月16日 23:23:34
  • 转载请务必保留本文链接:https://go.coder-hub.com/64392040.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定