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

发布于 2024-07-14 20:30:34 字数 4604 浏览 7 评论 0原文

在此问题中,TofuBeer 在创建泛型 IterableEnumeration 时遇到问题。

答案来自 jcrossley3 指向此链接 http://www.javaspecialists.eu/archive/Issue107 .html 基本上解决了问题。

还有一件事我不明白。 正如埃里克森有效指出的那样,真正的问题是:

构造参数化类型时不能指定通配符

但是在声明中删除通配符也不起作用:

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

导致以下错误:

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

但是 JavaSpecialist 中的示例确实有效:

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

我能做到的唯一区别值得注意的是,在 JavaSpecialists 博客中,Enumeration 来自一个 Vector,其签名为:

public Enumeration<E> elements()

而失败的则来自 ZipFile,其签名为:

public Enumeration<? extends ZipEntry> entries()

最后,所有这些都被链接中建议的 for-each 构造和静态 make 方法吸收了

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

但是!! 该新闻通讯的重点不是解决这个问题,而是避免仅仅因为语法看起来很丑就需要指定泛型类型!

所以..我的问题是:

发生了什么事?

当参数是类型为 < 的 Enumeration 时,为什么创建 IterableEnumeration 实例不起作用? 扩展 SomeClass> ? 为什么 make for-each 结构会解决这个问题?!!!

为什么这有效:

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

但这不起作用?

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

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

我想理解它!

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

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(2

靑春怀旧 2024-07-21 20:30:34

我不确定 foreach 循环发生了什么,但您需要将通配符添加到 IterableEnumeration 的声明中,以接受从 ZipFile.entries() 返回的未指定类型。

代替

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

用。。。来

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

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);
    }
左耳近心 2024-07-21 20:30:34

这里的根本问题是,当 ZipFile 被更改为支持泛型,维护者选择将 entries() 方法的返回类型设置为 Enumeration (大概是为了让子类 JarFile 中的方法可以返回 Enumeration)。 这会导致您所看到的问题。

因为 Enumeration 是协变使用的(因为它始终如此 - 它只返回值),所以您应该始终使方法参数采用 Enumeration

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

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文