我如何处理函数< T,R>和省略/ varargs在这种情况下? [英] How do I deal with Function<T, R> and ellipsis/varargs in this case?

查看:125
本文介绍了我如何处理函数< T,R>和省略/ varargs在这种情况下?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我的一个项目是 throw-lambdas ;在其中我的目标是在 Stream 中轻松使用潜在的 @FunctionalInterface s,其唯一的缺陷是在流中使用的是它们抛出已检查的异常(就我而言,我宁愿称有缺陷的事实是你不能从流中抛出已检查的异常但这是另一个故事)。

One of my project is throwing-lambdas; in it I aim to ease the use of potential @FunctionalInterfaces in Streams, whose only "defect" for being used in streams is that they throw checked exceptions (on my part I'd rather call defective the fact that you can't throw checked exceptions from streams but that's another story).

因此,对于函数< T,R> 我定义了这个:

So, for Function<T, R> I define this:

@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);
        }
    }
}

这允许我定义,例如:

final ThrowingFunction<Path, Path> = Path::toRealPath;

(为什么 Path :: toRealPath ..好吧,正是因为它有一个省略号)。

(why Path::toRealPath... Well, precisely because it has an ellipsis).

我不想在这里停下来我希望能够写下这样的东西:

Not wanting to stop here I want to be able to write stuff like:

Throwing.function(Path::toRealPath).fallbackTo(Path::toAbsolutePath)

以上几乎可以......继续阅读。

The above NEARLY works... Read on.

我也定义了这个:

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);
    }
}

这是<$ c的实现$ c>函数 s:

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);
            }
        };
    }
}

到目前为止一切顺利(虽然 IDEA无法识别 orTryWith()的代码为有效,但这是另一个故事。)

So far so good (although IDEA fails to recognize the code of orTryWith() as valid, but that's another story).

我还定义了一个名为投掷的实用程序类,问题在于我作为测试写的这个类的 main()

I also define a utility class called Throwing and the problem lies in the main() of this class which I wrote as a test:

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);
    }
}

现在,上面代码的错误消息是:

Now, the error message for the code above is:

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

我无法在此处诊断出错误的确切原因但对我来说,它看起来就像省略号一样;事实上,如果我这样做:

I can't diagnose the exact cause of the error here, but to me it just looks like ellipsis gets in the way; in fact, if I do:

    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);
    }

然后编译:所以这意味着 Stream。 map()确认结果为函数 ...

then it compiles: so it means that Stream.map() does acknowledge the result as being a Function...

所以为什么不会 Throwing.function(Path :: toRealPath)。fallbackTo(Path :: toAbsolutePath) compile?

So why won't Throwing.function(Path::toRealPath).fallbackTo(Path::toAbsolutePath) compile?

推荐答案

您的代码片段

Function<Path, Path> f = function(Path::toRealPath).fallbackTo(Path::toAbsolutePath);

正在达到规范中包含的Java 8类型推断的限制,因此它不是编译器错误。链接方法调用时,目标类型不起作用。由于链的第一个方法是varargs方法,因此需要其目标类型来查找预期的调用签名。这种情况类似于编写 p-> p.toRealPath()时的情况,其中调用的参数数量是明确的,但的类型p 未知。两者都不能在调用链中工作(除了在最后一次调用中)

is hitting a limitation of Java 8’s type inference which is contained in the specification, so it’s not a compiler bug. Target typing does not work when you chain method invocations. Since the first method of the chain is a varargs method, its target type is required to find the intended invocation signature. This situation similar to when you write p->p.toRealPath(), where the number of parameters of the invocation is unambiguous but the type of p is not known. Both won’t work in an invocation chain (besides in the last invocation)

这可以通过使第一次调用的类型显式来解决,

This can be resolved by either making the type of the first invocation explicit,

Function<Path, Path> f = Throwing.<Path,Path>function(Path::toRealPath)
  .fallbackTo(Path::toAbsolutePath);

ThrowingFunctionChain<Path, Path> f0 = function(Path::toRealPath);
Function<Path, Path> f = f0.fallbackTo(Path::toAbsolutePath);

Function<Path, Path> f = function((Path p)->p.toRealPath())
    .fallbackTo(Path::toAbsolutePath);

或将方法调用链转换为非链式方法调用在这里描述

or by converting the method invocation chain into unchained method invocations as described here:

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);
}

当一次调用以结果为目标时,规范故意拒绝两次调用的类型推断另一个,但如果相同的表达式只是另一个调用的参数,它就有效。

The specification deliberately denied the type inference for two invocations when one invocation targets the result of the other but it works if the same expressions are just parameters of another invocation.

这篇关于我如何处理函数&lt; T,R&gt;和省略/ varargs在这种情况下?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆