引发异常的 Java 8 Lambda 函数?

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/18198176/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-08-11 23:13:17  来源:igfitidea点击:

Java 8 Lambda function that throws exception?

javalambdajava-8

提问by Triton Man

I know how to create a reference to a method that has a Stringparameter and returns an int, it's:

我知道如何创建对具有String参数并返回 的方法的引用int,它是:

Function<String, Integer>

However, this doesn't work if the function throws an exception, say it's defined as:

但是,如果函数抛出异常,这将不起作用,比如它被定义为:

Integer myMethod(String s) throws IOException

How would I define this reference?

我将如何定义这个参考?

采纳答案by jason

You'll need to do one of the following.

您需要执行以下操作之一。

  • If it's your code, then define your own functional interface that declares the checked exception:

    @FunctionalInterface
    public interface CheckedFunction<T, R> {
       R apply(T t) throws IOException;
    }
    

    and use it:

    void foo (CheckedFunction f) { ... }
    
  • Otherwise, wrap Integer myMethod(String s)in a method that doesn't declare a checked exception:

    public Integer myWrappedMethod(String s) {
        try {
            return myMethod(s);
        }
        catch(IOException e) {
            throw new UncheckedIOException(e);
        }
    }
    

    and then:

    Function<String, Integer> f = (String t) -> myWrappedMethod(t);
    

    or:

    Function<String, Integer> f =
        (String t) -> {
            try {
               return myMethod(t);
            }
            catch(IOException e) {
                throw new UncheckedIOException(e);
            }
        };
    
  • 如果是您的代码,则定义您自己的功能接口来声明已检查的异常:

    @FunctionalInterface
    public interface CheckedFunction<T, R> {
       R apply(T t) throws IOException;
    }
    

    并使用它:

    void foo (CheckedFunction f) { ... }
    
  • 否则,包装Integer myMethod(String s)在一个不声明检查异常的方法中:

    public Integer myWrappedMethod(String s) {
        try {
            return myMethod(s);
        }
        catch(IOException e) {
            throw new UncheckedIOException(e);
        }
    }
    

    进而:

    Function<String, Integer> f = (String t) -> myWrappedMethod(t);
    

    或者:

    Function<String, Integer> f =
        (String t) -> {
            try {
               return myMethod(t);
            }
            catch(IOException e) {
                throw new UncheckedIOException(e);
            }
        };
    

回答by Adam R. Nelson

Disclaimer: I haven't used Java 8 yet, only read about it.

免责声明:我还没有使用过 Java 8,只是阅读了它。

Function<String, Integer>doesn't throw IOException, so you can't put any code in it that throws IOException. If you're calling a method that expects a Function<String, Integer>, then the lambda that you pass to that method can't throw IOException, period. You can either write a lambda like this (I think this is the lambda syntax, not sure):

Function<String, Integer>不会抛出IOException,所以你不能在里面放任何代码throws IOException。如果您正在调用一个需要 a 的方法Function<String, Integer>,那么您传递给该方法的 lambda 不能抛出IOException,句号。您可以编写这样的 lambda(我认为这是 lambda 语法,不确定):

(String s) -> {
    try {
        return myMethod(s);
    } catch (IOException ex) {
        throw new RuntimeException(ex);
        // (Or do something else with it...)
    }
}

Or, if the method you're passing the lambda to is one you wrote yourself, you can define a new functional interface and use that as the parameter type instead of Function<String, Integer>:

或者,如果您将 lambda 传递给的方法是您自己编写的方法,您可以定义一个新的函数式接口并将其用作参数类型而不是Function<String, Integer>

public interface FunctionThatThrowsIOException<I, O> {
    O apply(I input) throws IOException;
}

回答by assylias

This is not specific to Java 8. You are trying to compile something equivalent to:

这不是 Java 8 特有的。您正在尝试编译与以下内容等效的内容:

interface I {
    void m();
}
class C implements I {
    public void m() throws Exception {} //can't compile
}

回答by yohan

Another solution using a Function wrapper would be to return either an instance of a wrapper of your result, say Success, if everything went well, either an instance of, say Failure.

使用函数包装器的另一种解决方案是返回结果的包装器实例,例如成功,如果一切顺利,则是失败的实例。

Some code to clarify things :

一些代码来澄清事情:

public interface ThrowableFunction<A, B> {
    B apply(A a) throws Exception;
}

public abstract class Try<A> {

    public static boolean isSuccess(Try tryy) {
        return tryy instanceof Success;
    }

    public static <A, B> Function<A, Try<B>> tryOf(ThrowableFunction<A, B> function) {
        return a -> {
            try {
                B result = function.apply(a);
                return new Success<B>(result);
            } catch (Exception e) {
                return new Failure<>(e);
            }
        };
    }

    public abstract boolean isSuccess();

    public boolean isError() {
        return !isSuccess();
    }

    public abstract A getResult();

    public abstract Exception getError();
}

public class Success<A> extends Try<A> {

    private final A result;

    public Success(A result) {
        this.result = result;
    }

    @Override
    public boolean isSuccess() {
        return true;
    }

    @Override
    public A getResult() {
        return result;
    }

    @Override
    public Exception getError() {
        return new UnsupportedOperationException();
    }

    @Override
    public boolean equals(Object that) {
        if(!(that instanceof Success)) {
            return false;
        }
        return Objects.equal(result, ((Success) that).getResult());
    }
}

public class Failure<A> extends Try<A> {

    private final Exception exception;

    public Failure(Exception exception) {
        this.exception = exception;
    }

    @Override
    public boolean isSuccess() {
        return false;
    }

    @Override
    public A getResult() {
        throw new UnsupportedOperationException();
    }

    @Override
    public Exception getError() {
        return exception;
    }
}

A simple use case :

一个简单的用例:

List<Try<Integer>> result = Lists.newArrayList(1, 2, 3).stream().
    map(Try.<Integer, Integer>tryOf(i -> someMethodThrowingAnException(i))).
    collect(Collectors.toList());

回答by mmounirou

What I'm doing is to allow the user to give the value he actually want in case of exception . So I've something looking like this

我正在做的是允许用户在异常情况下给出他实际想要的值。所以我有这样的东西

public static <T, R> Function<? super T, ? extends R> defaultIfThrows(FunctionThatThrows<? super T, ? extends R> delegate, R defaultValue) {
    return x -> {
        try {
            return delegate.apply(x);
        } catch (Throwable throwable) {
            return defaultValue;
        }
    };
}

@FunctionalInterface
public interface FunctionThatThrows<T, R> {
    R apply(T t) throws Throwable;
}

And this can then be call like :

然后可以这样调用:

defaultIfThrows(child -> child.getID(), null)

回答by jlb

You can actually extend Consumer(and Functionetc.) with a new interface that handles exceptions -- using Java 8's default methods!

您实际上可以使用处理异常的新接口扩展ConsumerFunction等等)——使用 Java 8 的默认方法

Consider this interface (extends Consumer):

考虑这个接口(扩展Consumer):

@FunctionalInterface
public interface ThrowingConsumer<T> extends Consumer<T> {

    @Override
    default void accept(final T elem) {
        try {
            acceptThrows(elem);
        } catch (final Exception e) {
            // Implement your own exception handling logic here..
            // For example:
            System.out.println("handling an exception...");
            // Or ...
            throw new RuntimeException(e);
        }
    }

    void acceptThrows(T elem) throws Exception;

}

Then, for example, if you have a list:

然后,例如,如果您有一个列表:

final List<String> list = Arrays.asList("A", "B", "C");

If you want to consume it (eg. with forEach) with some code that throws exceptions, you would traditionally have set up a try/catch block:

如果你想forEach用一些抛出异常的代码来使用它(例如 with ),你传统上会设置一个 try/catch 块:

final Consumer<String> consumer = aps -> {
    try {
        // maybe some other code here...
        throw new Exception("asdas");
    } catch (final Exception ex) {
        System.out.println("handling an exception...");
    }
};
list.forEach(consumer);

But with this new interface, you can instantiate it with a lambda expression and the compiler will not complain:

但是使用这个新接口,您可以使用 lambda 表达式实例化它,编译器不会抱怨:

final ThrowingConsumer<String> throwingConsumer = aps -> {
    // maybe some other code here...
    throw new Exception("asdas");
};
list.forEach(throwingConsumer);

Or even just cast it to be more succinct!:

或者甚至只是将其转换为更简洁!:

list.forEach((ThrowingConsumer<String>) aps -> {
    // maybe some other code here...
    throw new Exception("asda");
});

Update: Looks like there's a very nice utility library part of Duriancalled Errorswhich can be used to solve this problem with a lot more flexibility. For example, in my implementation above I've explicitly defined the error handling policy (System.out...or throw RuntimeException), whereas Durian's Errors allow you to apply a policy on the fly via a large suite of utility methods. Thanks for sharing it, @NedTwigg!.

更新:看起来Durian有一个非常好的实用程序库部分,称为Errors,可用于以更大的灵活性解决此问题。例如,在我上面的实现中,我明确定义了错误处理策略 (System.out...throw RuntimeException),而 Durian 的错误允许您通过大量实用方法即时应用策略。感谢分享,@NedTwigg!。

Sample usage:

示例用法:

list.forEach(Errors.rethrow().wrap(c -> somethingThatThrows(c)));

回答by fge

This problem has been bothering me as well; this is why I have created this project.

这个问题也一直困扰着我;这就是我创建这个项目的原因

With it you can do:

有了它,您可以:

final ThrowingFunction<String, Integer> f = yourMethodReferenceHere;

There are a totla of 39 interfaces defined by the JDK which have such a Throwingequivalent; those are all @FunctionalInterfaces used in streams (the base Streambut also IntStream, LongStreamand DoubleStream).

JDK 总共定义了 39 个接口,它们具有这样的Throwing等效项;这些都是@FunctionalInterface在流中使用的 s(基础Stream还有IntStream,LongStreamDoubleStream)。

And as each of them extend their non throwing counterpart, you can directly use them in lambdas as well:

由于它们每个都扩展了它们的非抛出对应物,因此您也可以直接在 lambdas 中使用它们:

myStringStream.map(f) // <-- works

The default behavior is that when your throwing lambda throws a checked exception, a ThrownByLambdaExceptionis thrown with the checked exception as the cause. You can therefore capture that and get the cause.

默认行为是,当您抛出的 lambda 抛出已检查的异常时,ThrownByLambdaException会以已检查的异常作为原因抛出 a 。因此,您可以捕获它并找出原因。

Other features are available as well.

其他功能也可用。

回答by Franky Knuckels

public void frankTest() {
    int pageId= -1;

    List<Book> users= null;
    try {
        //Does Not Compile:  Object page=DatabaseConnection.getSpringConnection().queryForObject("SELECT * FROM bookmark_page", (rw, n) -> new Portal(rw.getInt("id"), "", users.parallelStream().filter(uu -> uu.getVbid() == rw.getString("user_id")).findFirst().get(), rw.getString("name")));

        //Compiles:
        Object page= DatabaseConnection.getSpringConnection().queryForObject("SELECT * FROM bookmark_page", (rw, n) -> { 
            try {
                final Book bk= users.stream().filter(bp -> { 
                    String name= null;
                    try {
                        name = rw.getString("name");
                    } catch (Exception e) {
                        // TODO Auto-generated catch block
                        e.printStackTrace();
                    }
                    return bp.getTitle().equals(name); 
                }).limit(1).collect(Collectors.toList()).get(0);
            } catch (Exception e) {
                // TODO Auto-generated catch block
                e.printStackTrace();
            }
            return new Portal(rw.getInt("id"), "", users.get(0), rw.getString("name")); 
        } );
    } catch (Exception e) {
        // TODO Auto-generated catch block
        e.printStackTrace();
    }
}

回答by Sergio

I had this problem with Class.forName and Class.newInstance inside a lambda, so I just did:

我在 lambda 中遇到了 Class.forName 和 Class.newInstance 的问题,所以我只是做了:

public Object uncheckedNewInstanceForName (String name) {

    try {
        return Class.forName(name).newInstance();
    }
    catch (ClassNotFoundException | InstantiationException | IllegalAccessException e) {
        throw new RuntimeException(e);
    }
}

Inside the lambda, instead of calling Class.forName("myClass").newInstance() I just called uncheckedNewInstanceForName ("myClass")

在 lambda 中,我没有调用 Class.forName("myClass").newInstance() 我只是调用了 uncheckedNewInstanceForName ("myClass")

回答by Ned Twigg

I think Durian's Errorsclasscombines many of the pros of the various suggestions above.

我认为Durian 的Errors课程结合了上述各种建议的许多优点。

To include Durian in your project, you can either:

要将 Durian 包含在您的项目中,您可以: