2009-03-07 9 views
3

Dans ce question, TofuBeer avait des problèmes pour créer un IterableEnumeration générique.Comment réparer cette erreur générique Java génériques?

La réponse est venue de jcrossley3 pointant vers ce lien http://www.javaspecialists.eu/archive/Issue107.html qui a à peu près résolu le problème.

Il y a encore une chose que je ne comprends pas. Le vrai problème, aussi efficacement signalé par erickson, était que:

You cannot specify a wildcard when constructing a parameterized type

Mais enlever le caractère générique dans la déclaration ne fonctionne pas non plus:

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

Résultats dans 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 échantillons dans le JavaSpecialist fonctionnent:

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

La seule différence que je peux repérer est que dans le blog de JavaSpecialists, le Enumeration vient d'une Vector dont la signature est:

public Enumeration<E> elements() 

tandis que celui qui ne vient de ZipFile dont la signature est:

public Enumeration<? extends ZipEntry> entries() 

Enfin, tout ceci 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 point dans cette newsletter n'était pas de résoudre ce problème, mais d'éviter la nécessité de spécifier un type générique, juste parce que la syntaxe semble moche !!

Alors .. mes questions est:

Qu'est-ce qui se passe?

Pourquoi ne crée-t-il pas une instance de IterableEnumeration lorsque le paramètre est Enumeration dont le type est <? extends SomeClass>? Et pourquoi la construction de chaque construction avale-t-elle le problème?

Pourquoi ce travail:

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

mais cela fonctionne pas?

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

Ci-dessous est une version modifiée (légèrement) 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 comprendre !!

Répondre

3

Je ne suis pas sûr de ce qui se passe avec la boucle foreach, mais vous devez ajouter le caractère générique à votre déclaration d'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); 
    } 
+0

: -o j'ai essayé ça !!! N'ai-je pas: -/ – OscarRyz

+0

: - O. . . ... – OscarRyz

+0

Oui, mais j'ai également modifié la méthode make(). – Matthew

0

Le problème sous-jacent ici est que lorsque ZipFile a été changé pour soutenir les génériques, le mainteneur a choisi de faire le type de retour des entries() méthodes Enumeration<? extends ZipEntry> (sans doute pour que la méthode de la sous-classe JarFile peut renvoyer Enumeration<JarEntry>). Cela provoque le problème que vous voyez.

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

Questions connexes