fge*_*fge 4 java lambda variadic-functions java-8 java-stream
我的一个项目是throwing-lambdas;在其中,我的目标是简化@FunctionalInterfaces 中潜在s的使用Stream,其在流中使用的唯一“缺陷”是它们抛出已检查的异常(就我而言,我宁愿称其为有缺陷的事实,即您不能抛出已检查的异常来自流,但那是另一个故事)。
所以,因为Function<T, R>我定义了这个:
@FunctionalInterface
public interface ThrowingFunction<T, R>
extends Function<T, R>
{
R doApply(T t)
throws Throwable;
default R apply(T t)
{
try {
return doApply(t);
} catch (Error | RuntimeException e) {
throw e;
} catch (Throwable throwable) {
throw new ThrownByLambdaException(throwable);
}
}
}
Run Code Online (Sandbox Code Playgroud)
这允许我定义,例如:
final ThrowingFunction<Path, Path> = Path::toRealPath;
Run Code Online (Sandbox Code Playgroud)
(为什么Path::toRealPath......好吧,正是因为它有一个省略号)。
不想停在这里,我希望能够写出这样的东西:
Throwing.function(Path::toRealPath).fallbackTo(Path::toAbsolutePath)
Run Code Online (Sandbox Code Playgroud)
以上几乎有效......继续阅读。
我也这样定义:
public abstract class Chainer<N, T extends N, C extends Chainer<N, T, C>>
{
protected final T throwing;
protected Chainer(final T throwing)
{
this.throwing = throwing;
}
public abstract C orTryWith(T other);
public abstract <E extends RuntimeException> T orThrow(
final Class<E> exclass);
public abstract N fallbackTo(N fallback);
public final <E extends RuntimeException> T as(final Class<E> exclass)
{
return orThrow(exclass);
}
}
Run Code Online (Sandbox Code Playgroud)
这是它对Functions的实现:
public final class ThrowingFunctionChain<T, R>
extends Chainer<Function<T, R>, ThrowingFunction<T, R>, ThrowingFunctionChain<T, R>>
implements ThrowingFunction<T, R>
{
public ThrowingFunctionChain(final ThrowingFunction<T, R> function)
{
super(function);
}
@Override
public R doApply(final T t)
throws Throwable
{
return throwing.doApply(t);
}
@Override
public ThrowingFunctionChain<T, R> orTryWith(
final ThrowingFunction<T, R> other)
{
final ThrowingFunction<T, R> function = t -> {
try {
return throwing.doApply(t);
} catch (Error | RuntimeException e) {
throw e;
} catch (Throwable ignored) {
return other.doApply(t);
}
};
return new ThrowingFunctionChain<>(function);
}
@Override
public <E extends RuntimeException> ThrowingFunction<T, R> orThrow(
final Class<E> exclass)
{
return t -> {
try {
return throwing.doApply(t);
} catch (Error | RuntimeException e) {
throw e;
} catch (Throwable throwable) {
throw ThrowablesFactory.INSTANCE.get(exclass, throwable);
}
};
}
@Override
public Function<T, R> fallbackTo(final Function<T, R> fallback)
{
return t -> {
try {
return doApply(t);
} catch (Error | RuntimeException e) {
throw e;
} catch (Throwable ignored) {
return fallback.apply(t);
}
};
}
}
Run Code Online (Sandbox Code Playgroud)
到目前为止一切顺利(尽管IDEA 无法将 的代码识别orTryWith()为有效,但那是另一回事了)。
我还定义了一个名为的实用程序类Throwing,问题在于main()我作为测试编写的这个类:
public final class Throwing
{
private Throwing()
{
throw new Error("nice try!");
}
public static <T, R> ThrowingFunctionChain<T, R> function(
final ThrowingFunction<T, R> function)
{
return new ThrowingFunctionChain<>(function);
}
public static void main(final String... args)
{
// FAILS TO COMPILE
final Function<Path, Path> f = function(Path::toRealPath)
.fallbackTo(Path::toAbsolutePath);
}
}
Run Code Online (Sandbox Code Playgroud)
现在,上面代码的错误信息是:
Error:(29, 48) java: incompatible types: cannot infer type-variable(s) T,R
(argument mismatch; invalid method reference
method toRealPath in interface java.nio.file.Path cannot be applied to given types
required: java.nio.file.LinkOption[]
found: java.lang.Object
reason: varargs mismatch; java.lang.Object cannot be converted to java.nio.file.LinkOption)
Error:(29, 49) java: invalid method reference
non-static method toRealPath(java.nio.file.LinkOption...) cannot be referenced from a static context
Error:(30, 25) java: invalid method reference
non-static method toAbsolutePath() cannot be referenced from a static context
Run Code Online (Sandbox Code Playgroud)
我无法在这里诊断错误的确切原因,但对我来说,它只是看起来像省略号;事实上,如果我这样做:
final ThrowingFunctionChain<Path, Path> f = function(Path::toRealPath);
try (
final Stream<Path> stream = Files.list(Paths.get(""));
) {
stream.map(f.fallbackTo(Path::toAbsolutePath))
.forEach(System.out::println);
}
Run Code Online (Sandbox Code Playgroud)
然后它编译:所以这意味着Stream.map()确实承认结果是Function......
那为什么不Throwing.function(Path::toRealPath).fallbackTo(Path::toAbsolutePath)编译呢?
正如 Holger 在评论中所说的,在链接方法时,编译器的类型推断是有限的。只需提供一个明确的类型参数
final Function<Path, Path> f = Throwing.<Path, Path>function(Path::toRealPath).fallbackTo(Path::toAbsolutePath);
Run Code Online (Sandbox Code Playgroud)
你的代码片段
Function<Path, Path> f = function(Path::toRealPath).fallbackTo(Path::toAbsolutePath);
Run Code Online (Sandbox Code Playgroud)
遇到了规范中包含的 Java 8 类型推断的限制,因此它不是编译器错误。链接方法调用时,目标类型不起作用。由于链的第一个方法是可变参数方法,因此需要其目标类型才能找到预期的调用签名。这种情况类似于您编写 时p->p.toRealPath(),调用的参数数量明确但类型p未知。两者都不会在调用链中工作(除了最后一次调用)
这可以通过使第一次调用的类型显式来解决,
Function<Path, Path> f = Throwing.<Path,Path>function(Path::toRealPath)
.fallbackTo(Path::toAbsolutePath);
Run Code Online (Sandbox Code Playgroud)
或者
ThrowingFunctionChain<Path, Path> f0 = function(Path::toRealPath);
Function<Path, Path> f = f0.fallbackTo(Path::toAbsolutePath);
Run Code Online (Sandbox Code Playgroud)
或者
Function<Path, Path> f = function((Path p)->p.toRealPath())
.fallbackTo(Path::toAbsolutePath);
Run Code Online (Sandbox Code Playgroud)
或者通过将方法调用链转换为非链式方法调用,如下所述:
public static <T, R> ThrowingFunctionChain<T, R> function(
final ThrowingFunction<T, R> function)
{
return new ThrowingFunctionChain<>(function);
}
public static <T, R> Function<T, R> function(
final ThrowingFunction<T, R> function, Function<T, R> fallBack)
{
return new ThrowingFunctionChain<>(function).fallbackTo(fallBack);
}
public static void main(final String... args)
{
Function<Path, Path> f = function(Path::toRealPath, Path::toAbsolutePath);
}
Run Code Online (Sandbox Code Playgroud)
当一个调用以另一个调用的结果为目标时,规范故意拒绝对两次调用进行类型推断,但如果相同的表达式只是另一个调用的参数,则它可以工作。