关于泛型:是否有任何Java标准类在没有实现Collection的情况下实现Iterable?

Are there any Java standard classes that implement Iterable without implementing Collection?

我有一个难题,让我思考是否有任何标准的Java类实现EDOCX1 0,而不需要实现EDCOX1 1。我正在实现一个接口,它要求我定义一个接受Iterable的方法,但是我用来支持此方法的对象需要一个Collection

这让我做了一些非常笨拙的感觉代码,在编译时给出一些未经检查的警告。

1
2
3
4
5
6
7
8
9
10
11
12
public ImmutableMap<Integer, Optional<Site>> loadAll(
        Iterable<? extends Integer> keys
) throws Exception {
    Collection<Integer> _keys;
    if (keys instanceof Collection) {
        _keys = (Collection<Integer>) keys;
    } else {
        _keys = Lists.newArrayList(keys);
    }

    final List<Site> sitesById = siteDBDao.getSitesById(_keys);
    // snip: convert the list to a map

将生成的集合更改为使用更通用的Collection类型不会消除该行的未选中警告。另外,我不能将方法签名更改为接受Collection而不是Iterable,因为这样它就不再覆盖超级方法,并且在需要时不会被调用。

在这个问题上似乎没有办法:其他问题在其他地方被问到,它似乎深深地植根于Java的泛型和类型删除系统中。但我想问的是,是否有任何类可以实现Iterable,而不实现Collection?我已经浏览了Iterablejavadoc,当然,我希望传递到接口的所有内容实际上都是一个集合。我想使用一个在野外,预先编写的类代替,因为它似乎更可能实际地作为一个参数传递,并将使单元测试更有价值。

我确信,由于我正在编写一些单元测试,我已经编写了与我在项目中使用的类型相关的强制转换或复制位。但是我想为一些输入编写一个单元测试,这些输入是一个不可迭代的集合,到目前为止,我所能想到的就是自己实现一个虚拟的测试类实现。

出于好奇,我正在实现的方法是guava的CacheLoader.loadAll(Iterable keys),而支持方法是一个jdbi实例化的数据访问对象,它需要一个集合作为@BindIn接口的参数类型。我认为我认为这是正确的,这与问题是相切的,但以防万一有人想尝试横向思考我的问题。我知道我只需分叉JDBI项目并重写@BindIn注释就可以接受一个不可重写的…


虽然没有一个类可以立即满足您的需求,并且对测试代码的读者是直观的,但是您可以轻松地创建自己的匿名类,这很容易理解:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
static Iterable<Integer> range(final int from, final int to) {
    return new Iterable<Integer>() {
        public Iterator<Integer> iterator() {
            return new Iterator<Integer>() {
                int current = from;
                public boolean hasNext() { return current < to; }
                public Integer next() {
                    if (!hasNext()) { throw new NoSuchElementException(); }
                    return current++;
                }
                public void remove() { /*Optional; not implemented.*/ }
            };
        }
    };
}

演示。

此实现是匿名的,不实现Collection。另一方面,它生成一个非空的可枚举整数序列,您可以完全控制它。


克劳迪,是的,但我认为代码

1
2
3
4
5
6
Collection<Integer> _keys;
if (keys instanceof Collection) {
    _keys = (Collection<Integer>) keys;
} else {
    _keys = Lists.newArrayList(keys);
}

完美无瑕。接口Collection扩展了Iterable,不允许用两个不同的类型参数实现同一个接口,例如,类无法实现CollectionIterable

Integer类是最终的,因此IterableIterable之间的差异很大程度上是学术性的。

综上所述,最后2段证明,如果某个东西既是一个Iterable又是一个Collection,那么它一定是一个Collection。因此,您的代码保证是安全的。编译器不能确定这一点,因此可以通过编写

1
@SuppressWarnings("unchecked")

在声明上方。您还应该在注释中包含一个注释来解释为什么代码是安全的。

至于是否有实现Iterable而不是Collection的类的问题,正如其他人指出的答案是肯定的。不过,我认为您真正要问的是,拥有两个接口是否有意义。其他许多人都问过这个问题。通常,当一个方法有一个Collection参数(例如addAll())时,它可以,也可能应该是Iterable

编辑

安德烈亚斯在评论中指出,EDCOX1的引用13只是在Java 5中引入的,而EDCOX1×2的引用是在Java 1.2中引入的,并且由于兼容的原因,无法使用EDOCX1×2的大多数现有方法进行EDCOX1 13的修改。


按标题回答问题:

Are there any Java standard classes that implement Iterable without implementing Collection?

从文本:

If there ever are any classes that can implement Iterable that don't also implement Collection?

答:

是的

请参见以下javadoc页面:https://docs.oracle.com/javase/8/docs/api/java/lang/class-use/iterable.html

任何表示EDCOX1(1)的部分都将列出实现接口的Java标准类。其中许多都没有实现Collection


在核心API中,只有Iterable而不是Collection的类型。--

1
2
3
4
5
6
7
8
interface java.nio.file.Path

interface java.nio.file.DirectoryStream
interface java.nio.file.SecureDirectoryStream

class java.util.ServiceLoader

class java.sql.SQLException (and subclasses)

可以说,这些都是不好的设计。


正如@ Buy.IO的回答中提到的那样,EDOCX1 13的一个实现是在Java 7中引入的文件系统遍历的新的EDCOX1×25类。

如果您恰巧在Java 8上,EDCOX1 OR 13(已经给出了EDCOX1×27方法)EDCOX1,28(注意它的实现注释),它允许您使用它与EDCOX1,29。

1
2
3
4
5
6
public static <T> Collection<T> convert(Iterable<T> iterable) {
    // using Collectors.toList() for illustration,
    // there are other collectors available
    return StreamSupport.stream(iterable.spliterator(), false)
                        .collect(Collectors.toList());
}

这样做的代价很小,任何已经是Collection实现的参数都会经过不必要的流和收集操作。与您最初的Casting或基于guava的方法相比,您可能只应在希望使用标准化的JDK-Only方法超过潜在的性能冲击时使用它,因为您已经在使用guava的CacheLoader,这可能是不太现实的。

要对此进行测试,请考虑此代码段和示例输出:

1
2
3
4
// Snippet
System.out.println(convert(Paths.get(System.getProperty("java.io.tmpdir"))));
// Sample output on Windows
[Users, MyUserName, AppData, Local, Temp]

在阅读了优秀的答案和提供的文档之后,我又在几节课上翻了翻,找到了看起来是赢家的东西,包括测试代码的直截了当性和直截了当的问题标题。Java的主要EDCOX1×0实现包含此GEM:

1
2
3
public Iterator<E> iterator() {
    return new Itr();
}

其中Itr是一个私有的内部类,具有高度优化的、定制的Iterator实现。不幸的是,Iterator本身并没有实现Iterable,所以如果我想将horn-i t放入辅助方法中测试不执行强制转换的代码路径,我必须将它包装在自己的垃圾类中,该类实现Iterable,而不是Collection,并返回Itr。这是一种简便的方法,可以轻松地将集合转换为可ITerable,而无需自己编写迭代代码。

最后一点,我的最终版本的代码甚至都不进行强制转换,因为guava的Lists.newArrayList几乎与我在问题中对运行时类型检测所做的完全相同。

1
2
3
4
5
6
7
8
9
10
11
12
@GwtCompatible(serializable = true)
public static <E> ArrayList<E> More ...newArrayList(Iterable<? extends E> elements) {
  checkNotNull(elements); // for GWT
  // Let ArrayList's sizing logic work, if possible
  if (elements instanceof Collection) {
    @SuppressWarnings("unchecked")
    Collection<? extends E> collection = (Collection<? extends E>) elements;
    return new ArrayList<E>(collection);
  } else {
    return newArrayList(elements.iterator());
  }
}