Mad*_*nan 5 java generics lambda java-8 java-stream
我创建了一个将zip文件存档合并到一个存档中的实用程序.在这样做时,我最初有以下方法(请参阅此问题以获取一些背景信息ExceptionWrapper
):
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)));
}
Run Code Online (Sandbox Code Playgroud)
下面是代码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;
}
Run Code Online (Sandbox Code Playgroud)
这会导致编译错误:
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
但是,以下更改编译正常并按预期工作:
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);
}
Run Code Online (Sandbox Code Playgroud)
请注意,我所做的就是将内联创建的内容Consumer
转换为单独的变量.任何规范专家都知道在内联时编译器会发生什么变化Consumer
?
编辑:根据要求,这是签名addEntryContent(...)
:
private void addEntryContent(final ZipOutputStream out,
final ZipFile source,
final ZipEntry entry,
final Set<String> entryNames) throws IOException {
Run Code Online (Sandbox Code Playgroud)
问题是相当不寻常的签名ZipFile.stream()
:
public Stream<? extends ZipEntry> stream()
Run Code Online (Sandbox Code Playgroud)
它是这样定义的,因为它允许子类JarFile
来覆盖与签名吧:
public Stream<JarEntry> stream()
Run Code Online (Sandbox Code Playgroud)
现在当你打电话stream()
给a时,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表达式被推断而失败,因为lambda表达式被认为与目标类型不兼容Consumer<? super ? extends ZipEntry>
.
使用类型的临时变量时Consumer<ZipEntry>
,您已明确定义了lambda表达式的类型,并且该类型与目标类型兼容.或者,您可以通过以下方式使lambda表达式的类型更明确:
source.stream().forEach(ExceptionWrapper.wrapConsumer(
(ZipEntry e) -> addEntryContent(out, source, e, entryNames)));
Run Code Online (Sandbox Code Playgroud)
或者只是使用JarFile
而不是ZipFile
.该JarFile
不介意底层文件是一个普通的zip文件(即没有症状).由于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)));
Run Code Online (Sandbox Code Playgroud)
当然,它现在会推断出类型,Consumer<JarEntry>
而不是Consumer<ZipEntry>
差异没有后果......
归档时间: |
|
查看次数: |
162 次 |
最近记录: |