Question

Dans cette question , TofuBeer avait des problèmes pour créer un fichier générique IterableEnumeration.

La réponse est venue de jcrossley3 en indiquant ce lien http://www.javaspecialists.eu/ archive / Issue107.html qui a pratiquement résolu le problème.

Il y a encore une chose que je ne comprends pas. Comme le soulignait effectivement Erickson, le vrai problème était que:

  

Vous ne pouvez pas spécifier de caractère générique lors de la construction d'un type paramétré

Mais supprimer le caractère générique dans la déclaration n'a pas fonctionné non plus:

final IterableEnumeration<ZipEntry> iteratable 
                  = new IterableEnumeration<ZipEntry>(zipFile.entries());

Résultats de l'erreur suivante:

Main.java:19: cannot find symbol
symbol  : constructor IterableEnumeration(java.util.Enumeration<capture#469 of ? extends java.util.zip.ZipEntry>)
location: class IterableEnumeration<java.util.zip.ZipEntry>
        final IterableEnumeration<ZipEntry> iteratable = new IterableEnumeration<ZipEntry>(  zipFile.entries());
                                                         ^
1 error

Mais les exemples de JavaSpecialist fonctionnent:

  IterableEnumeration<String> ie =
              new IterableEnumeration<String>(sv.elements());

La seule différence que je peux remarquer est que dans le blog JavaSpecialists, le Enumeration provient d'un Vector dont la signature est:

public Enumeration<E> elements()

alors que celui qui échoue provient de ZipFile dont la signature est:

public Enumeration<? extends ZipEntry> entries()

Enfin, tout cela est absorbé par la construction for-each et la méthode make statique suggérée dans le lien

.
for(final ZipEntry entry : IterableEnumeration.make( zipFile.entries() ))  {
    if(!(entry.isDirectory())) {
        names.add(entry.getName());
    }
}

Mais !! le but de cette lettre d’information n’était pas de résoudre ce problème, mais d’éviter de spécifier un type générique, tout simplement parce que la syntaxe était moche !!

Donc .. mes questions sont:

Que se passe-t-il?

Pourquoi la création d'une instance de <? extends SomeClass> ne fonctionne-t-elle pas lorsque le paramètre est un <=> dont le type est <=>? Et pourquoi la construction «make for-each» engloutit-elle le problème? !!!

Pourquoi cela fonctionne-t-il:

for(final ZipEntry entry : IterableEnumeration.make( zipFile.entries() ))  {

mais ça ne marche pas?

final IterableEnumeration<ZipEntry> iteratable
                     = IterableEnumeration.make( zipFile.entries() );

Ci-dessous une version (légèrement) modifiée du code original de TofuBeer:

import java.util.Enumeration;
import java.util.HashSet;
import java.util.Iterator;
import java.util.Set;
import java.util.zip.ZipEntry;
import java.util.zip.ZipFile;
import java.util.Vector;

public class Main {
    private ZipFile zipFile;

    public Set<String> entries() {

        final Vector<ZipEntry>    vector = new Vector<ZipEntry>();
        // why this works.
        //final IterableEnumeration<ZipEntry> iteratable = new IterableEnumeration<ZipEntry>( vector.elements() );

        // but this do not.
        //final IterableEnumeration<ZipEntry> iteratable = new IterableEnumeration<ZipEntry>( zipFile.entries() );

        // nor this 
        final IterableEnumeration<ZipEntry> iteratable = IterableEnumeration.make( zipFile.entries() );

        // And what's with the for-each that doesn't care about the type?    
        final Set<String>   names = new HashSet<String>();

        for(final ZipEntry entry : IterableEnumeration.make( zipFile.entries() ))  {
            if(!(entry.isDirectory())) {
                names.add(entry.getName());
            }
        }

        return (names);
    }
}

class IterableEnumeration<T> implements Iterable<T> {
    private final Enumeration<T> enumeration;

    public IterableEnumeration(final Enumeration<T> e) {
        enumeration = e;
    }

    public Iterator<T> iterator() {
        return new Iterator<T>() { 
             public boolean hasNext() {
                return (enumeration.hasMoreElements());
            }

            public T next() {
                return (enumeration.nextElement());
            }

            public void remove() {
                throw new UnsupportedOperationException("Cannot remove via an Enumeration");
            }
        };
    }
    // As suggested by http://www.javaspecialists.eu/archive/Issue107.html
    // but doesn't help with: final IterableEnumeration<ZipEntry> iteratable = IterableEnumeration.make( zipFile.entries() );
    public static <T> Iterable<T> make(Enumeration<T> en) {
        return new IterableEnumeration<T>(en);
    }
}

Je veux le comprendre !!

Était-ce utile?

La solution

Je ne sais pas ce qu'il se passe dans la boucle foreach, mais vous devez ajouter le caractère générique à votre déclaration IterableEnumeration pour accepter le type non spécifié renvoyé par ZipFile.entries ().

Remplacer

    private final Enumeration<T> enumeration;

    public IterableEnumeration(final Enumeration<T> e) {
        enumeration = e;
    }

    public static <T> Iterable<T> make(Enumeration<T> en) {
        return new IterableEnumeration<T>(en);
    }

Avec

    private final Enumeration<? extends T> enumeration;

    public IterableEnumeration(final Enumeration<? extends T> e) {
        enumeration = e;
    }

    public static <T> Iterable<T> make(Enumeration<? extends T> en) {
        return new IterableEnumeration<T>(en);
    }

Autres conseils

Le problème sous-jacent est que lorsque Le fichier ZipFile ayant été modifié pour prendre en charge les génériques, le responsable a choisi de définir le type de retour des entries() méthodes Enumeration<? extends ZipEntry> (afin que la méthode de la sous-classe JarFile puisse renvoyer Enumeration<JarEntry>). Cela cause le problème que vous voyez.

Comme Enumeration<T> est utilisé de manière covariante (comme toujours - il ne renvoie que des valeurs), vous devez toujours faire en sorte que les arguments de méthode prennent Enumeration<? extends T>.

Licencié sous: CC-BY-SA avec attribution
Non affilié à StackOverflow
scroll top