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

Function<String, Integer>

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

Integer myMethod(String s) throws IOException

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


当前回答

我会做一些一般的事情:

public interface Lambda {

    @FunctionalInterface
    public interface CheckedFunction<T> {

        T get() throws Exception;
    }

    public static <T> T handle(CheckedFunction<T> supplier) {
        try {
            return supplier.get();
        } catch (Exception exception) {
            throw new RuntimeException(exception);

        }
    }
}

用法:

 Lambda.handle(() -> method());

其他回答

然而,你可以创建自己的FunctionalInterface,抛出如下..

@FunctionalInterface
public interface UseInstance<T, X extends Throwable> {
  void accept(T instance) throws X;
}

然后使用Lambdas或引用实现它,如下所示。

import java.io.FileWriter;
import java.io.IOException;

//lambda expressions and the execute around method (EAM) pattern to
//manage resources

public class FileWriterEAM  {
  private final FileWriter writer;

  private FileWriterEAM(final String fileName) throws IOException {
    writer = new FileWriter(fileName);
  }
  private void close() throws IOException {
    System.out.println("close called automatically...");
    writer.close();
  }
  public void writeStuff(final String message) throws IOException {
    writer.write(message);
  }
  //...

  public static void use(final String fileName, final UseInstance<FileWriterEAM, IOException> block) throws IOException {

    final FileWriterEAM writerEAM = new FileWriterEAM(fileName);    
    try {
      block.accept(writerEAM);
    } finally {
      writerEAM.close();
    }
  }

  public static void main(final String[] args) throws IOException {

    FileWriterEAM.use("eam.txt", writerEAM -> writerEAM.writeStuff("sweet"));

    FileWriterEAM.use("eam2.txt", writerEAM -> {
        writerEAM.writeStuff("how");
        writerEAM.writeStuff("sweet");      
      });

    FileWriterEAM.use("eam3.txt", FileWriterEAM::writeIt);     

  }


 void writeIt() throws IOException{
     this.writeStuff("How ");
     this.writeStuff("sweet ");
     this.writeStuff("it is");

 }

}

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

Function<String, Integer>不抛出IOException,所以你不能在其中放入任何抛出IOException的代码。如果你正在调用一个期望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...)
    }
}

或者,如果你传递lambda的方法是你自己写的,你可以定义一个新的函数接口,并使用它作为参数类型,而不是Function<String, Integer>:

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

实际上,您可以使用一个处理异常的新接口来扩展Consumer(和Function等)——使用Java 8的默认方法!

考虑这个接口(扩展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;

}

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

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

如果你想消费它(例如。使用forEach)和一些抛出异常的代码,你通常会设置一个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);

但是有了这个新接口,你可以用lambda表达式实例化它,编译器不会报错:

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

或者甚至只是转换它更简洁!:

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

更新

看起来榴莲有一个非常好的实用程序库,叫做Errors,可以用来解决这个问题,具有更大的灵活性。例如,在上面的实现中,我显式地定义了错误处理策略(System.out…或者抛出RuntimeException),而Durian的Errors允许您通过一套大型实用程序方法动态应用策略。感谢分享,@NedTwigg!

示例用法:

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

创建一个自定义返回类型,该类型将传播已检查的异常。这是创建一个新接口的替代方案,该新接口映射现有的函数接口,只需在函数接口的方法上稍微修改一个“抛出异常”。

定义

CheckedValueSupplier

public static interface CheckedValueSupplier<V> {
    public V get () throws Exception;
}

CheckedValue

public class CheckedValue<V> {
    private final V v;
    private final Optional<Exception> opt;

    public Value (V v) {
        this.v = v;
    }

    public Value (Exception e) {
        this.opt = Optional.of(e);
    }

    public V get () throws Exception {
        if (opt.isPresent()) {
            throw opt.get();
        }
        return v;
    }

    public Optional<Exception> getException () {
        return opt;
    }

    public static <T> CheckedValue<T> returns (T t) {
        return new CheckedValue<T>(t);
    }

    public static <T> CheckedValue<T> rethrows (Exception e) {
        return new CheckedValue<T>(e);
    }

    public static <V> CheckedValue<V> from (CheckedValueSupplier<V> sup) {
        try {
            return CheckedValue.returns(sup.get());
        } catch (Exception e) {
            return Result.rethrows(e);
        }
    }

    public static <V> CheckedValue<V> escalates (CheckedValueSupplier<V> sup) {
        try {
            return CheckedValue.returns(sup.get());
        } catch (Exception e) {
            throw new RuntimeException(e);
        }
    }

}

使用

//  Don't use this pattern with FileReader, it's meant to be an
//  example.  FileReader is a Closeable resource and as such should
//  be managed in a try-with-resources block or in another safe
//  manner that will make sure it is closed properly.

//  This will not compile as the FileReader constructor throws
//  an IOException.
    Function<String, FileReader> sToFr =
        (fn) -> new FileReader(Paths.get(fn).toFile());

// Alternative, this will compile.
    Function<String, CheckedValue<FileReader>> sToFr = (fn) -> {
        return CheckedValue.from (
            () -> new FileReader(Paths.get("/home/" + f).toFile()));
    };

// Single record usage
    // The call to get() will propagate the checked exception if it exists.
    FileReader readMe = pToFr.apply("/home/README").get();


// List of records usage
    List<String> paths = ...; //a list of paths to files
    Collection<CheckedValue<FileReader>> frs =
        paths.stream().map(pToFr).collect(Collectors.toList());

// Find out if creation of a file reader failed.
    boolean anyErrors = frs.stream()
        .filter(f -> f.getException().isPresent())
        .findAny().isPresent();

这是怎么呢

创建一个抛出检查异常的功能接口(checkedvaluesprovider)。这将是唯一允许检查异常的功能接口。所有其他功能接口都将利用checkedvaluesprovider来包装抛出检查异常的任何代码。

CheckedValue类将保存抛出检查异常的任何逻辑的执行结果。这可以防止已检查异常的传播,直到代码试图访问CheckedValue实例所包含的值。

这种方法的问题。

我们现在抛出“异常”,有效地隐藏了最初抛出的特定类型。 在调用CheckedValue#get()之前,我们不知道发生了异常。

消费者等

一些功能接口(例如Consumer)必须以不同的方式处理,因为它们不提供返回值。

函数代替消费者

一种方法是使用函数而不是消费者,后者在处理流时应用。

    List<String> lst = Lists.newArrayList();
// won't compile
lst.stream().forEach(e -> throwyMethod(e));
// compiles
lst.stream()
    .map(e -> CheckedValueSupplier.from(
        () -> {throwyMethod(e); return e;}))
    .filter(v -> v.getException().isPresent()); //this example may not actually run due to lazy stream behavior

升级

或者,您总是可以升级为RuntimeException。还有其他的答案涵盖了从Consumer内部升级已检查异常。

不消费。

只需要避免所有的函数接口,并使用一个老式的for循环。

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

在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.