我创建了一个实用程序,可以将 zip 文件存档合并到一个存档中。为此,我最初使用了以下方法(有关 ExceptionWrapper
的一些背景信息,请参阅 this question):
private void addFile(File f, final ZipOutputStream out, final Set<String> entryNames){
ZipFile source = getZipFileFromFile(f);
source.stream().forEach(ExceptionWrapper.wrapConsumer(e -> addEntryContent(out, source, e, entryNames)));
}
这是 ExceptionWrapper.wrapConsumer
和 ConsumerWrapper
的代码
public static <T> Consumer<T> wrapConsumer(ConsumerWrapper<T> consumer){
return t -> {
try {
consumer.accept(t);
} catch (Exception e) {
throw new IllegalStateException(e);
}
};
}
public interface ConsumerWrapper<T>{
void accept(T t) throws Exception;
}
这会导致编译错误:
Error:(128, 62) java: incompatible types: java.util.function.Consumer<capture#1 of ? extends java.util.zip.ZipEntry> cannot be converted to java.util.function.Consumer<? super capture#1 of ? extends java.util.zip.ZipEntry> Error:(128, 97) java: incompatible types: java.lang.Object cannot be converted to java.util.zip.ZipEntry
However, the following change compiles just fine and works as expected:
private void addFile(File f, final ZipOutputStream out, final Set<String> entryNames){
ZipFile source = getZipFileFromFile(f);
Consumer<ZipEntry> consumer = ExceptionWrapper.wrapConsumer(e -> addEntryContent(out, source, e, entryNames));
source.stream().forEach(consumer);
}
请注意,我所做的只是将 Consumer
的内联创建提取到一个单独的变量中。任何规范专家都知道当内联 Consumer
时编译器会发生什么变化?
编辑:根据要求,这是 addEntryContent(...)
的签名:
private void addEntryContent(final ZipOutputStream out,
final ZipFile source,
final ZipEntry entry,
final Set<String> entryNames) throws IOException {
最佳答案
问题是 ZipFile.stream()
的签名相当不寻常:
public Stream<? extends ZipEntry> stream()
这样定义是因为它允许子类JarFile
至 override它带有签名:
public Stream<JarEntry> stream()
现在当您调用 stream()
时在 ZipFile
上你得到一个Stream<? extends ZipEntry>
用forEach
具有有效签名的方法 void forEach(Consumer<? super ? extends ZipEntry> action)
这是对类型推断的挑战。
通常,对于 Consumer<? super T>
的目标类型, 函数签名 T → void
得到推断并得到 Consumer<T>
与 Consumer<? super T>
的目标类型兼容.但是当涉及通配符时,它会作为 Consumer<? extends ZipEntry>
失败。推断您的 lambda 表达式被认为与目标类型不兼容 Consumer<? super ? extends ZipEntry>
.
当您使用 Consumer<ZipEntry>
类型的临时变量时,您已经明确定义了 lambda 表达式的类型,并且该类型与目标类型兼容。或者,您可以通过以下方式使 lambda 表达式的类型更明确:
source.stream().forEach(ExceptionWrapper.wrapConsumer(
(ZipEntry e) -> addEntryContent(out, source, e, entryNames)));
或者简单地使用 JarFile
而不是 ZipFile
. JarFile
不介意底层文件是否是普通 zip 文件(即没有 list )。自 JarFile.stream()
不使用通配符,类型推断没有问题:
JarFile source = getZipFileFromFile(f);// have to adapt the return type of that method
source.stream().forEach(ExceptionWrapper.wrapConsumer(
e -> addEntryContent(out, source, e, entryNames)));
当然,它现在会推断类型 Consumer<JarEntry>
而不是 Consumer<ZipEntry>
但这种差异没有任何后果......
关于java - 为什么内联 Consumer<ZipEntry> 编译失败但在外部工作?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/31455188/