上限通配符、迭代器和 for-each 循环

4
我不明白为什么这段代码可以编译通过:
public void test(Collection<? extends Enum> enumCollection) {
    for (Enum e : enumCollection) {
    }
}

这一个没有:

public void test(Collection<? extends Enum> enumCollection) {
    Iterator<Enum> iterator = enumCollection.iterator();
    //Required: Iterator<Java.lang.Enum>
    //Found: Iterator<capture<? extends Java.lang.Enum>>
}

? extends Enum 在所有情况下都应该是 Enum 的子类型,那么为什么我在使用迭代器时会出现编译错误,而在使用 for-each 循环时却可以正常工作呢?


1
可能是因为 ArrayList<Object> foo = new ArrayList<String>(); 不起作用的原因与 Object bar = new ArrayList<String>().get(0); 可以正常工作的原因相同。 - tobias_k
为什么不能使用Collection<Enum>而非Collection<? extends Enum> - Braj
Iterator<Enum> 表示你知道 enumCollectionEnum 类型,但是 Collection<? extends Enum> enumCollection 表明 enumCollection 也可以是 Enum 的子类型。 - Number945
2个回答

8

我经常发现这句格言适用于IT技术领域:如果你需要在泛型中使用 ? ,那么你可能正在做一些不正确的事情

以下代码是正确的:

public <T extends Enum<T>> void test(Collection<T> enumCollection) {
    for (Enum e : enumCollection) {
    }
    Iterator<T> iterator = enumCollection.iterator();
}

您的问题在于您期望泛型像普通代码一样,其中ArrayList也是一个List。但实际上它并非为此而设计。泛型旨在确保您使用相同类型,而不仅仅是兼容的类型

在您的情况下 - 要匹配? extends Enum,您可以使用:

    Iterator<?> iterator = enumCollection.iterator();

这个很好用。

你的类型不匹配,泛型就是为了确保它们匹配。

记住,? 不意味着“我不在乎它是什么类型”,而是意味着“我不想知道它是什么类型”。在泛型类型中使用 ?,然后期望能够利用该类型是很愚蠢的。


这个很好用,但如果你想将一个 Collection<MyCustomEnum> 传递给 test,就不行了。 - tobias_k
@tobias_k - 仍然有效!enum MyCustomEnum {A, B;}test(Arrays.asList(MyCustomEnum.values()))都可以正常工作。 - OldCurmudgeon
@tobias_k - 这不仅适用于“枚举”类型。在泛型中,“X extends Y”的意思是 绝对 必须 extend Y,一个 Y 无法做到。然后捕获实际类型 (如果使用了 ?, 则未捕获)。 - OldCurmudgeon
没错,这就是我在(d)案例中所指的,也是为什么我认为这在我第一条评论中行不通的原因。然而,我想我们在这里互相误解了。不过,谢谢你关于枚举类型的见解。 - tobias_k
1
@user2572030 - 因为泛型是用于精确匹配类型的。即使Object o = "Hello"是有效的Java代码,但List<String>并不等同于List<Object> - OldCurmudgeon
显示剩余5条评论

3

关于for循环,根据JLS 14.14.2,以下代码:

public void test(Collection<? extends Enum> enumCollection) {
    for (Enum e : enumCollection) {
    }
}

将被翻译为以下内容:

public <T extends Enum> void test(Collection<T> enumCollection) {
    for (Iterator<T> iterator = enumCollection.iterator(); iterator.hasNext();) {
        Enum e = (T) iterator.next();
    }
}

这段代码可以成功编译。正如你所看到的,您永远不会直接拥有Iterator<Enum>,而是拥有一个Iterator,其元素类型与您集合中指定的类型相同。

JLS 中相关部分为:

如果 Expression 的类型是 Iterable 的子类型,其类型参数为 X,则令 I 为类型 java.util.Iterator;否则,令 I 为原始类型 java.util.Iterator。

Enum 变量中只针对每个元素进行存储,而且只在相关情况下进行。


感谢您指出JLS。我以为它会让它变得清晰明了,但它明确表示List<? extends Integer> l被翻译为Iterator<Integer> #i = l.iterator()。这正是我的问题所在:它被翻译成的代码无法编译。(我将其复制到我的IDE中,删除#) - user2572030

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