为什么 Collections.UnmodifiableMap.UnmodifiableEntrySet 覆盖 stream() 方法?

Why does Collections.UnmodifiableMap.UnmodifiableEntrySet override the stream() method?

在查看源代码时,我可以看到 stream() 方法已在 Collections.UnmodifiableMap.UnmodifiableEntrySet 中被覆盖。但是代码似乎与 Collection.stream() 相同,除了 Collections.UnmodifiableMap.UnmodifiableEntrySet.stream() 中的 return 类型更具体为 Stream<Entry<K,V>> 而不是 Collection.stream() 中的 Stream<E> =].

spliterator() 方法在 类 中是不同的,但即使 stream 没有被覆盖,我认为 UnmodifiableEntrySet.spliterator() 会从 [=15= 调用] 如果对象的类型是 UnmodifiableEntrySet.

那么,stream 方法被覆盖有什么原因吗?

Collection.java

@Override
default Spliterator<E> spliterator() {
    return Spliterators.spliterator(this, 0);
}
 
default Stream<E> stream() {
    return StreamSupport.stream(spliterator(), false);
}

Collections.UnmodifiableMap.UnmodifiableEntrySet.java

@SuppressWarnings("unchecked")
public Spliterator<Entry<K,V>> spliterator() {
    return new UnmodifiableEntrySetSpliterator<>(
        (Spliterator<Map.Entry<K, V>>) c.spliterator());
}

@Override
public Stream<Entry<K,V>> stream() {
    return StreamSupport.stream(spliterator(), false);
}

Collections.java 的内容复制到新的 class CollectionsCopy.java 后,我尝试从 UnmodifiableEntrySet 中删除 stream 方法,然后我进行了一些调试并且能够得到答案。

UnmodifiableEntrySet 扩展了 UnmodifiableSet,后者扩展了 UnmodifiableCollection,后者又实现了 Collection。由于 UnmodifiableCollection 有一个字段 final Collection<? extends E> c;,它必须覆盖 stream() 如下

public Stream<E> stream() {
    return (Stream<E>)c.stream();
}

UnmodifiableEntrySet 的构造函数中,Set<? extends Map.Entry<? extends K, ? extends V>> 对象被转换为原始 Set 类型并传递给 super 构造函数。

super((Set)s);

附加到代码的评论是:

Need to cast to raw in order to work around a limitation in the type system

因此,当我从 UnmodifiableEntrySet 中删除 stream()(在我的 CollectionsCopy.java 中)时,由于原始类型转换,调用的 spliterator 方法是Set 而不是 UnmodifiableEntrySet 中的那个。这是因为 UnmodifiableCollection 中的字段 c 将是 Set 而不是 UnmodifiableEntrySet 并且当从 UnmodifiableCollection.stream() 调用 c.stream() 时,它会调用 Set.spliterator().因此,UnmodifiableEntrySet 有必要覆盖 UnmodifiableCollectionstream() 的实现。

此外,我尝试从 UnmodifiableCollection 中删除覆盖的 stream()。这一次,它按预期工作,因为 spliterator 是从 UnmodifiableEntrySet.

调用的

编辑: 根据一些评论,建议从 Collection 返回的流不会是 Entry 的流,而只是 Stream。但在这种情况下,我不应该能够从流上的 Entry class 调用任何方法。但是我能够在该流上调用 map(Entry::getValue) 。所以,返回的流确实是 Entry.

类型的流

以下Java文档/程序来自openjdk 14 2020-03-17.

覆盖 spliteratorstream 的主要原因是确保 UnmodifiableEntrySet 的条目未被修改。

来自 UnmodifiableEntrySet 的评论:

We need this class in addition to UnmodifiableSet as Map.Entries themselves permit modification of the backing Map via their setValue operation. This class is subtle: there are many possible attacks that must be thwarted.

首先,UnmodifiableEntrySet 扩展了 UnmodifiableSet,后者扩展了 UnmodifiableCollection。 在UnmodifiableCollection中,代理模式用于避免修改backing Collection c,大多数方法只是调用backing Collection方法,比如spliteratorstream:

    @Override
    public Spliterator<E> spliterator() {
        return (Spliterator<E>)c.spliterator();
    }

    @SuppressWarnings("unchecked")
    @Override
    public Stream<E> stream() {
        return (Stream<E>)c.stream();
    }

因此,如果 UnmodifiableEntrySet 不重写这些方法,行为将遵循 UnmodifiableCollection 实现,并且支持条目将被公开并可以通过 Entry#setValue.[=47 进行修改=]

因此 spliteratorstream 方法被覆盖并引入 UnmodifiableEntrySetSpliterator 以使用 UnmodifiableEntry 包装对支持条目的所有访问,确保无法修改条目.

为什么 UnmodifiableCollection 覆盖 stream

似乎没有必要覆盖 UnmodifiableCollection 中的 stream,因为我们可以使用 Collection 中的默认实现(只需通过 spliterator 创建流). 但是作者决定使用后备 Collection c stream 方法覆盖 stream,可能的原因之一是后备 Collection 可能出于性能原因覆盖 stream 方法,例如Collections.CopiesList,或者spliterator方法不符合Collection#stream

的要求

This method should be overridden when the spliterator() method cannot return a spliterator that is IMMUTABLE, CONCURRENT, or late-binding. (See spliterator() for details.)