gpt4 book ai didi

java - 为什么内联 Consumer 编译失败但在外部工作?

转载 作者:搜寻专家 更新时间:2023-10-31 19:54:13 24 4
gpt4 key购买 nike

我创建了一个实用程序,可以将 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.wrapConsumerConsumerWrapper 的代码

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

这样定义是因为它允许子类JarFileoverride它带有签名:

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/

24 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com