views:

614

answers:

2

In this question, TofuBeer was having problems creating a genericized IterableEnumeration.

The answer came from jcrossley3 pointing to this link http://www.javaspecialists.eu/archive/Issue107.html which pretty much solved the problem.

There is still one thing I don't get. The real problem, as effectively pointed out by erickson, was that:

You cannot specify a wildcard when constructing a parameterized type

But removing the wildcard in the declaration didn't work either:

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

Results in the following error:

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

But the samples in the JavaSpecialist do work:

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

The only difference I can spot is that in the JavaSpecialists blog, the Enumeration comes from a Vector whose signature is:

public Enumeration<E> elements()

while the one that fails comes from ZipFile whose signature is:

public Enumeration<? extends ZipEntry> entries()

Finally, all of this is absorbed by the for-each construct and the static make method suggested in the link

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

But!! the point in that newsletter was not to solve this problem, but to avoid the need to specify a generic type, just because the syntax looks ugly!!

So.. my questions is:

What is happening?

Why doesn't creating an instance of IterableEnumeration work when the parameter is an Enumeration whose type is <? extends SomeClass> ? And why does the make for-each construct swallow the problem?!!!

Why does this work:

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

but this not work?

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

Below is a (slightly) modified version of TofuBeer's original code:

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

I want to understand it!!

+3  A: 

I'm not sure what's happening with the foreach loop, but you need to add the wildcard to your declaration of IterableEnumeration to accept the unspecified type returned from ZipFile.entries().

Replace

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

With

    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);
    }
Matthew
:-o I tried that!!! Didn't I :-/
OscarRyz
: - O . . . .. .
OscarRyz
Yes, but I also modified the make() method.
Matthew
A: 

The underlying problem here is that when ZipFile was changed to support generics, the maintainer chose to make the return type of the entries() methods Enumeration<? extends ZipEntry> (presumably so that the method in the subclass JarFile can return Enumeration<JarEntry>). This causes the problem you are seeing.

Because Enumeration<T> is used covariantly (as it is always - it only ever returns values), you should always make method arguments take Enumeration<? extends T>.

Ben Lings