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

Function<String, Integer>

然而,如果函数抛出异常,比如它被定义为:

Integer myMethod(String s) throws IOException

我该如何定义这个引用呢?


当前回答

如果您不介意使用第三方库,使用cyclops-react(我贡献的一个库),您可以使用FluentFunctions API来编写

 Function<String, Integer> standardFn = FluentFunctions.ofChecked(this::myMethod);

ofChecked接受jOOλ CheckedFunction,并将软化后的引用返回到标准(未选中)JDK java.util.function.Function。

或者,你可以通过FluentFunctions api继续使用捕获的函数!

例如,要执行你的方法,最多重试5次,并记录你可以写入的状态

  FluentFunctions.ofChecked(this::myMethod)
                 .log(s->log.debug(s),e->log.error(e,e.getMessage())
                 .try(5,1000)
                 .apply("my param");

其他回答

使用Function包装器的另一种解决方案是返回结果包装器的实例,如果一切正常,则返回成功,或者返回失败。

一些代码来澄清事情:

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

一个简单的用例:

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

这里已经贴出了很多很棒的回复。只是试图用不同的角度来解决问题。这只是我的两毛钱,如果我哪里说错了,请指正。

在FunctionalInterface中抛出子句不是一个好主意

我认为强制抛出IOException可能不是一个好主意,原因如下

This looks to me like an anti-pattern to Stream/Lambda. The whole idea is that the caller will decide what code to provide and how to handle the exception. In many scenarios, the IOException might not be applicable for the client. For example, if the client is getting value from cache/memory instead of performing actual I/O. Also, the exceptions handling in streams becomes really hideous. For example, here is my code will look like if I use your API acceptMyMethod(s -> { try { Integer i = doSomeOperation(s); return i; } catch (IOException e) { // try catch block because of throws clause // in functional method, even though doSomeOperation // might not be throwing any exception at all. e.printStackTrace(); } return null; }); Ugly isn't it? Moreover, as I mentioned in my first point, that the doSomeOperation method may or may not be throwing IOException (depending on the implementation of the client/caller), but because of the throws clause in your FunctionalInterface method, I always have to write the try-catch.

如果我知道这个API抛出IOException怎么办

Then probably we are confusing FunctionalInterface with typical Interfaces. If you know this API will throw IOException, then most probably you also know some default/abstract behavior as well. I think you should define an interface and deploy your library (with default/abstract implementation) as follows public interface MyAmazingAPI { Integer myMethod(String s) throws IOException; } But, the try-catch problem still exists for the client. If I use your API in stream, I still need to handle IOException in hideous try-catch block. Provide a default stream-friendly API as follows public interface MyAmazingAPI { Integer myMethod(String s) throws IOException; default Optional<Integer> myMethod(String s, Consumer<? super Exception> exceptionConsumer) { try { return Optional.ofNullable(this.myMethod(s)); } catch (Exception e) { if (exceptionConsumer != null) { exceptionConsumer.accept(e); } else { e.printStackTrace(); } } return Optional.empty(); } } The default method takes the consumer object as argument, which will be responsible to handle the exception. Now, from client's point of view, the code will look like this strStream.map(str -> amazingAPIs.myMethod(str, Exception::printStackTrace)) .filter(Optional::isPresent) .map(Optional::get).collect(toList()); Nice right? Of course, logger or other handling logic could be used instead of Exception::printStackTrace. You can also expose a method similar to https://docs.oracle.com/javase/8/docs/api/java/util/concurrent/CompletableFuture.html#exceptionally-java.util.function.Function- . Meaning that you can expose another method, which will contain the exception from previous method call. The disadvantage is that you are now making your APIs stateful, which means that you need to handle thread-safety and which will be eventually become a performance hit. Just an option to consider though.

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

这并不是Java 8所特有的。你正在尝试编译一些等价的东西:

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

如果你有lombok,你可以用@SneakyThrows注释你的方法

SneakyThrow不会静默地吞咽、包装到RuntimeException中,或以其他方式修改所列出的检查异常类型的任何异常。JVM不检查被检查异常系统的一致性;Javac可以,而且这个注释允许您选择不使用它的机制。

https://projectlombok.org/features/SneakyThrows