如何修复此Java泛型通配符错误?

时间:2022-11-07 19:02:33

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

在这个问题中,TofuBeer在创建泛化的IterableEnumeration时遇到了问题。

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

答案来自jcrossley3指向这个链接http://www.javaspecialists.eu/archive/Issue107.html几乎解决了这个问题。

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:

但JavaSpecialist中的示例确实有效:

  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:

我能发现的唯一区别是,在JavaSpecialists博客中,Enumeration来自Vector,其签名是:

public Enumeration<E> elements()

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

而失败的那个来自ZipFile,其签名是:

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-each构造和链接中建议的静态make方法所吸收

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?!!!

当参数是类型为 ?为什么make-each构造吞下了这个问题?!!!

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:

下面是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);
    }
}

I want to understand it!!

我想明白!!

2 个解决方案

#1


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().

我不确定foreach循环发生了什么,但是你需要在你的IterableEnumeration声明中添加通配符以接受从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);
    }

#2


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.

这里的根本问题是,当ZipFile被更改为支持泛型时,维护者选择使entry()方法的返回类型枚举 (可能是因为子类JarFile中的方法可以返回Enumeration )。这会导致您遇到的问题。

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>.

因为Enumeration 是共同使用的(因为它总是 - 它只返回值),所以你应该总是使方法参数采用Enumeration 。

#1


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().

我不确定foreach循环发生了什么,但是你需要在你的IterableEnumeration声明中添加通配符以接受从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);
    }

#2


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.

这里的根本问题是,当ZipFile被更改为支持泛型时,维护者选择使entry()方法的返回类型枚举 (可能是因为子类JarFile中的方法可以返回Enumeration )。这会导致您遇到的问题。

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>.

因为Enumeration 是共同使用的(因为它总是 - 它只返回值),所以你应该总是使方法参数采用Enumeration 。