为什么 Eclipse Compiler 会丢失固定类型参数?

Why does Eclipse Compiler lose fixed type parameter?

我为这个问题找了一个合适的标题,因为我观察到的现象很奇怪。因此,我跳过从字面上解释我的问题,而是向您展示一些(希望如此)self-describing 代码。考虑以下参数化 class:

public class GenericOptional<T> {

    public GenericOptional(T someValue) {}

    public T getValue() { return null; }

    public Optional<String> getOptionalString() { return Optional.empty(); }
}

我想强调的是方法getOptionalString()的return类型Optional<String>不依赖于type-parameter T.

现在看看下面的代码,它使用 Java 8u45Eclipse Luna 4.4.2 中编译:

public static void main(String[] args) {
    Object obj = new GenericOptional<>(Boolean.TRUE);
    GenericOptional go = (GenericOptional) obj;
    Optional os = go.getOptionalString();
}

局部变量 os 的类型 Optional 没有 type-parameter StringEclipse 编译器丢失了有关固定 type-parameter 的信息。有谁知道为什么?

现在看第二个代码示例:

public static void main(String[] args) {
    Object obj = new GenericOptional<>(Boolean.TRUE);
    GenericOptional<?> go = (GenericOptional) obj;
    Optional<String> os = go.getOptionalString();
}

通过将局部变量 go 声明为 GenericOptional<?>,方法 getOptionalString() 的 return 类型现在是预期的 Optional<String>

谁能解释一下这种行为?

这不是关于 Eclipse 或任何东西,而是关于原始类型。

让我们回顾一下这个片段:

public static void main(String[] args) {
    Object obj = new GenericOptional<>(Boolean.TRUE);
    GenericOptional go = (GenericOptional) obj;
    Optional os = go.getOptionalString();
}

在这里,您正在创建 GenericOptionalraw 实例,这意味着类型参数信息将被完全关闭。因此,实例化一个 raw GenericOptional 意味着该实例将公开如下方法:

public class GenericOptional {

    public GenericOptional(Object someValue) {}

    public Object getValue() { return null; }

    public Optional getOptionalString() { return Optional.empty(); }
}

但是,如果我们现在查看第二个片段

public static void main(String[] args) {
    Object obj = new GenericOptional<>(Boolean.TRUE);
    GenericOptional<?> go = (GenericOptional) obj;
    Optional<String> os = go.getOptionalString();
}

我们可以看到您正在创建 GenericOptional 的通用实例。即使它的类型参数是 <?>,编译器也不会关闭对类型参数的关注,因此该实例将公开参数化的 getOptionalString() 方法,如下所示:

public Optional<String> getOptionalString() { return Optional.empty(); }

您正面临 raw types 的行为。当您使用原始类型时,无论成员的泛型签名与 class 的类型参数之间是否存在联系,泛型都会被有效地 完全 关闭.

这背后的原因是 原始类型 是一种仅向后兼容预泛型代码的功能。所以要么你有泛型,要么没有。

如果Generic方法不依赖class的实际类型参数,问题很容易解决:

GenericOptional<?> go = (GenericOptional<?>) obj;
Optional<String> os = go.getOptionalString();

使用 <?> 意味着“我不知道实际的类型参数,我不在乎,但我正在使用通用类型检查”。