将泛型类转换为其泛型子类型

5
假设我有以下内容:
class Base<T> {}

class Derived<T> extends Base<T> {}

那么在我的代码中,我可以像这样安全地进行强制类型转换而不会收到警告:

public <T> void foo(Base<T> base) {
    Derived<T> f = (Derived<T>) base; // fine, no warning
}

这是可以的。但是如果派生类有更多类型参数,它就不再起作用了:

class Base<T> {}

class Derived<T, U> extends Base<T> {}

public <T> void foo(Base<T> base) {
    Derived<T, ?> f = (Derived<T, ?>) base; // unchecked warning!
}

为什么会这样呢?我有什么明显的漏洞吗?

为什么它不工作?对我来说它是可以工作的... - Random42
“不起作用”的是,尽管这个转换是完全安全的,但我收到了一个未经检查的警告。” - Jean-Philippe Pellet
什么编译器?我是用Eclipse编译器看到的。 - Paul Bellora
你使用的是哪个Java版本/IDE?我在Java 6 u 37和Intellij IDEA 12.1中没有收到任何未经检查的警告。 - Random42
1个回答

5
这对我来说似乎是一个bug。从JLS §5.5.2. Checked Casts and Unchecked Casts可以看出:
将类型S转换为参数化类型(§4.5)T的转换未经检查,除非满足以下至少一个条件:
- S <: T - T的所有类型参数(§4.5.1)都是无界通配符 - T <: S并且S没有子类型X,其中X的类型参数不包含在T的类型参数中,除了T本身。
给定您的类型Base<T>Derived<T, ?>作为ST,前两个条件显然不成立。
那就剩下第三个条件了——如果我们能够识别出除Derived<T, ?>以外的Base<T>子类型,且它们的类型参数不包含在Derived<T, ?>的类型参数中,则该条件不成立。如果警告是正确的,那么一定存在这样的子类型,但我找不到。例如,Derived<?, ?>不起作用,因为它不是Base<T>的子类型。

我曾犹豫是否要发布这个答案,因为Eclipse编译器在泛型方面有良好的记录,看到这个警告让我觉得可能我漏掉了什么。如果我误读了JLS,请评论或编辑我的答案以确保正确性。 - Paul Bellora
1
JDK 7没有发出警告。JLS 5/6没有强制要求警告。我认为jdt编译器开发人员只是错过了这个变化或者由于错误而忽略了它(应该是:“……没有子类型X,除了T之外,其中**|T|=|X|和** …” - Ben Schulz
@BenSchulz 是的,我也在想那个措辞 - 是否有关于此的JLS问题报告? - Paul Bellora
据我所知,针对这种小的技术细节很难追踪到。 - Ben Schulz

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接