Y a-t-il des classes Java standard qui implémentent Iterable sans implémenter Collection?

j'ai une énigme qui m'a amené à me demander s'il existe des classes java standard qui implémentent Iterable<T> sans implémenter aussi Collection<T> . J'implémente une interface qui me demande de définir une méthode qui accepte un Iterable<T> , mais l'objet que j'utilise pour supporter cette méthode nécessite un Collection<T> .

cela me fait faire un peu de code de sentiment vraiment kludgy qui donnent quelques avertissements non contrôlés une fois compilé.

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

changer ma collection résultante pour utiliser le type plus général Collection<? extends Integer> n'élimine pas l'avertissement non vérifié pour cette ligne. En outre, Je ne peux pas changer la signature de la méthode pour accepter un Collection au lieu d'un Iterable parce qu'alors il n'est plus en train d'outrepasser la super méthode et ne sera pas appelé si nécessaire.

Il y ne semble pas être un moyen de contourner cette distribution ou copie de problème: d'autres questions ont été demandé ici un ailleurs et il semble profondément enraciné dans les systèmes d'effacement générique et de type de Java. Mais je demande plutôt s'il y a des classes qui peuvent implémenter Iterable<T> qui n'implémentent pas aussi Collection<T> ? J'ai jeté un coup d'oeil à travers le Iterable JavaDoc et certainement tout ce que je m'attends à passer à mon interface sera en fait une collection. J'aimerais utiliser un cours pré-écrit dans la nature, car cela semble beaucoup plus susceptible d'être être passé en tant que paramètre et rendrait le test de l'unité que beaucoup plus précieux.

je suis certain que le cast-or-copy bit que j'ai écrit fonctionne avec les types pour lesquels je l'utilise dans mon projet en raison de certains tests unitaires que je suis en train d'écrire. Mais je voudrais écrire un test unitaire pour une entrée qui est un itérable encore n'est pas une collection et jusqu'à présent tout ce que j'ai pu trouver est la mise en œuvre d'une application de classe de test factice moi-même.


pour les curieux, la méthode que j'applique est CacheLoader<K, V>.loadAll(Iterable<? extends K> keys) CacheLoader<K, V>.loadAll(Iterable<? extends K> keys) de Guava et la méthode de support est un objet jdbi instanciated data-access, qui nécessite une collection pour être utilisé comme type de paramètre pour l'interface @BindIn . Je pense que j'ai raison de penser que c'est tangent à la question, mais juste au cas où quelqu'un voudrait essayer la pensée latérale sur mon problème. Je suis conscient que je pourrais il suffit de bifurquer le projet JDBI et réécrire l'annotation @BindIn pour accepter un itérable...

20
demandé sur Community 2015-09-14 20:31:21

6 réponses

bien qu'il n'y ait aucune classe qui serait immédiatement adapté à vos besoins et être intuitif pour les lecteurs de votre code de test, vous pouvez facilement créer votre propre classe anonyme qui est facile à comprendre:

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.*/ }
            };
        }
    };
}

Démo.

cette implémentation est anonyme, et elle n'implémente pas Collection<Integer> . D'autre part, il produit un non-vide énumérable séquence d'entiers, que vous pouvez contrôler complètement.

11
répondu dasblinkenlight 2015-09-14 17:44:44

Kludgy, Oui, mais je pense que le code

Collection<Integer> _keys;
if (keys instanceof Collection) {
    _keys = (Collection<Integer>) keys;
} else {
    _keys = Lists.newArrayList(keys);
}

est parfaitement sain. L'interface Collection<T> étend Iterable<T> et vous n'êtes pas autorisé à implémenter la même interface avec 2 paramètres de type différents, il n'y a donc pas moyen qu'une classe puisse implémenter Collection<String> et Iterable<Integer> , par exemple.

la classe Integer est finale, de sorte que la différence entre Iterable<? extends Integer> et Iterable<Integer> est largement académique.

pris ensemble, les deux derniers paragraphes prouvent que si quelque chose est à la fois un Iterable<? extends Integer> et un Collection , il doit s'agir d'un Collection<Integer> . Par conséquent, votre code est garanti pour être sûr. Le compilateur ne peut pas être sûr de cela donc vous pouvez supprimer l'avertissement en écrivant

@SuppressWarnings("unchecked")

au-dessus de la déclaration. Vous devez également inclure un commentaire par les annotations pour expliquer pourquoi le code est sûr.

quant à la question de savoir s'il y a toutes les classes qui implémentent Iterable , mais pas Collection , comme d'autres l'ont souligné, la réponse est oui. Cependant, je pense que ce que vous demandez vraiment, c'est s'il y a un intérêt à avoir deux interfaces. Beaucoup d'autres l'ont demandé. Souvent, lorsqu'une méthode a un argument Collection (par exemple addAll() ), il pourrait, et devrait probablement, être un Iterable .

Modifier

@Andreas a souligné dans les commentaires que Iterable n'a été introduit Qu'en Java 5, alors que Collection a été introduit en Java 1.2, et la plupart des méthodes existantes prenant un Collection ne pouvait pas être modifié pour prendre un Iterable pour des raisons de compatibilité.

10
répondu Paul Boddington 2015-09-15 01:46:38

pour répondre À la question que par le titre:

y a-t-il des classes Java standard qui implémentent Iterable sans implémenter Collection ?

à Partir du texte:

S'il y a des classes qui peuvent implémenter Iterable<T> qui n'implémentent pas aussi Collection<T> ?

réponse:

Oui

voir la page javadoc suivante: https://docs.oracle.com/javase/8/docs/api/java/lang/class-use/Iterable.html

toute section qui dit Classes in XXX that implement Iterable , listera les classes Java standard implémentant l'interface. Beaucoup d'entre eux ne mettent pas en œuvre Collection .

10
répondu Andreas 2015-09-15 08:21:31

dans les API du cœur, les seuls types qui sont Iterable mais pas 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)

ce sont sans doute de mauvaises conceptions.

5
répondu ZhongYu 2015-09-14 22:05:02

comme mentionné dans @bayou.io ' S réponse, l'une de ces implémentations pour Iterable est la nouvelle classe Path pour le système de fichiers transversal introduit en Java 7.

si vous êtes sur Java 8, Iterable a été modifié avec (i.e. donné une méthode default ) spliterator() (faites attention à sa note de mise en œuvre ), qui vous permet de l'utiliser en conjonction avec StreamSupport :

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());
}

cela vient à la légère dépense que tout argument qui est déjà une mise en œuvre Collection passe par une opération de flux et de collecte inutile. Vous ne devriez probablement l'utiliser que si le désir d'une approche standardisée JDK-seulement l'emporte sur le succès potentiel de performance, par rapport à votre casting original ou des méthodes basées sur la goyave, ce qui est probablement discutable puisque vous utilisez déjà le CacheLoader De Goyave .

pour tester ceci, considérez cet extrait et la sortie de l'échantillon:

// Snippet
System.out.println(convert(Paths.get(System.getProperty("java.io.tmpdir"))));
// Sample output on Windows
[Users, MyUserName, AppData, Local, Temp]
1
répondu h.j.k. 2017-05-23 10:28:35

après avoir lu les excellentes réponses et fourni des docs, j'ai fouillé dans quelques classes supplémentaires et j'ai trouvé ce qui semble être le gagnant, à la fois en termes de franchise pour le code de test et pour un titre de question directe. L'implémentation principale de Java ArrayList contient ce joyau:

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

Itr est une classe intérieure privée avec une mise en œuvre hautement optimisée et personnalisée de Iterator<E> . Malheureusement, Iterator ne met pas en œuvre lui-même Iterable , donc si je veux le saboter dans ma méthode d'aide pour tester le chemin de code qui ne fait pas le moulage, je dois l'envelopper dans ma propre classe de camelote qui met en œuvre Iterable (et non Collection ) et renvoie le Itr . C'est une façon pratique de transformer facilement une collection en une collection itérable sans avoir à écrire le code itératif vous-même.

sur une note finale, ma version finale du code ne fait même pas le se lancer, parce que Lists.newArrayList de Guava fait à peu près exactement ce que je faisais avec la détection de type runtime dans la question.

@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());
  }
}
0
répondu Patrick M 2016-01-12 16:48:57