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

Pat*_*k M 21 java generics collections iterable

我有一个难题,这让我思考是否有任何标准的java类在Iterable<T>没有实现的情况下实现Collection<T>.我正在实现一个接口,它要求我定义一个接受一个的方法Iterable<T>,但我用来支持这个方法的对象需要一个Collection<T>.

这让我做了一些真正的kludgy感觉代码,在编译时会给出一些未经检查的警告.

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
Run Code Online (Sandbox Code Playgroud)

更改生成的集合以使用更通用的Collection<? extends Integer>类型不会消除该行的未经检查的警告.此外,我无法更改方法签名以接受Collection而不是Iterable因为它然后它不再覆盖超级方法,并且在需要时不会被调用.

目前似乎没有被周围的方式该铸造或拷贝的问题:其他问题都得到了一个在别处问这里,似乎深深扎根于Java的泛型和类型擦除系统.但我要问的是,是否有任何可以实现的类Iterable<T>还没有实现Collection<T>?我已经浏览了IterableJavaDoc,当然我希望传递给我的界面的所有内容实际上都是一个集合.我想使用一个在野外预编写的类,因为它似乎更有可能实际作为参数传递,并使单元测试更有价值.

由于我正在写的一些单元测试,我确定我编写的演员或复制位与我在项目中使用它的类型一起工作.但我想编写一个单元测试的一些输入端,一个可迭代但不是一个集合,到目前为止,所有我已经能够拿出正在实施一个虚拟的测试类实现自己.


好奇的是,我正在实现的方法是Guava CacheLoader<K, V>.loadAll(Iterable<? extends K> keys),而支持方法是一个JDBI实例化的数据访问对象,它需要一个集合作为@BindIn接口的参数类型.我认为我认为这是问题的正确性是正确的,但以防万一有人想尝试横向思考我的问题.我知道我可以只分叉JDBI项目并重写@BindIn注释以接受可迭代的...

das*_*ght 12

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

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.*/ }
            };
        }
    };
}
Run Code Online (Sandbox Code Playgroud)

演示.

此实现是匿名的,并且不实现Collection<Integer>.另一方面,它产生一个非空的可枚举整数序列,您可以完全控制它.


And*_*eas 11

按标题回答问题:

是否有任何Java标准类在Iterable没有实现的情况下实现Collection

来自文字:

如果有任何可以实现的类Iterable<T>还没有实现Collection<T>

回答:

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

任何部分Classes in XXX that implement Iterable都会列出实现该接口的Java标准类.其中许多都没有实施Collection.


Pau*_*ton 10

Kludgy,是的,但我认为代码

Collection<Integer> _keys;
if (keys instanceof Collection) {
    _keys = (Collection<Integer>) keys;
} else {
    _keys = Lists.newArrayList(keys);
}
Run Code Online (Sandbox Code Playgroud)

听起来很完美.接口Collection<T>扩展Iterable<T>和你不能来实现与2点不同类型的参数相同的接口,所以没有办法一个类可以实现Collection<String>Iterable<Integer>,例如.

Integer是final,所以之间的区别Iterable<? extends Integer>,并Iterable<Integer>在很大程度上是学术性的.

总而言之,最后两段证明,如果某事既是a Iterable<? extends Integer>又是a Collection,它必须是a Collection<Integer>.因此,您的代码保证是安全的.编译器无法确定这一点,因此您可以通过编写来抑制警告

@SuppressWarnings("unchecked")
Run Code Online (Sandbox Code Playgroud)

在声明之上.您还应该在注释中包含注释,以解释代码安全的原因.

至于是否有任何课程实施Iterable但没有实施的问题Collection,正如其他人指出的那样,答案是肯定的.但是我认为你真正要问的是有两个接口是否有任何意义.许多人都问过这个.通常当一个方法有一个Collection参数时(例如addAll()它可能,也可能应该是一个参数)Iterable.

编辑

@Andreas在评论中指出,这些评论Iterable仅在Java 5 Collection中引入,而在Java 1.2中引入,并且由于兼容性原因,大多数采用a的现有方法Collection都无法进行改进Iterable.


Zho*_*gYu 5

在核心API中,唯一的类型是- Iterable但不是Collection-

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)
Run Code Online (Sandbox Code Playgroud)

可以说这些都是糟糕的设计.


Pat*_*k M 1

在阅读了出色的答案和提供的文档后,我又浏览了几个课程,发现无论是在测试代码的简单性还是直接问题标题方面,看起来都是赢家。Java 的主要ArrayList实现包含这个 gem:

public Iterator<E> iterator() {
    return new Itr();
}
Run Code Online (Sandbox Code Playgroud)

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

最后一点,我的代码的最终版本甚至没有进行强制转换本身,因为 Guava 所做的Lists.newArrayList几乎与我在问题中使用运行时类型检测所做的完全一样。

@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());
  }
}
Run Code Online (Sandbox Code Playgroud)