如何修复此 Java 泛型通配符错误?
在此问题中,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());
}
}
但是!! 该新闻通讯的重点不是解决这个问题,而是避免仅仅因为语法看起来很丑就需要指定泛型类型!
所以..我的问题是:
发生了什么事?
当参数是类型为 < 的
? 为什么 make for-each 结构会解决这个问题?!!!Enumeration
时,为什么创建 IterableEnumeration
实例不起作用? 扩展 SomeClass>
为什么这有效:
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 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(2)
我不确定 foreach 循环发生了什么,但您需要将通配符添加到 IterableEnumeration 的声明中,以接受从 ZipFile.entries() 返回的未指定类型。
代替
用。。。来
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
With
这里的根本问题是,当 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()
methodsEnumeration<? extends ZipEntry>
(presumably so that the method in the subclassJarFile
can returnEnumeration<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 takeEnumeration<? extends T>
.