我如何从Java 8 lambda内部抛出CHECKED异常,例如在流中使用?

换句话说,我想让代码像这样编译:

public List<Class> getClasses() throws ClassNotFoundException {     

    List<Class> classes = 
        Stream.of("java.lang.Object", "java.lang.Integer", "java.lang.String")
              .map(className -> Class.forName(className))
              .collect(Collectors.toList());                  
    return classes;
    }

这段代码无法编译,因为上面的Class.forName()方法会抛出ClassNotFoundException,该异常会被检查。

请注意,我不想将已检查异常包装在运行时异常中,并抛出已包装的未检查异常。我想抛出检查异常本身,而不向流添加丑陋的try/catch。


当前回答

你可以用apache commons-lang3库来实现。

https://commons.apache.org/proper/commons-lang/javadocs/api-release/org/apache/commons/lang3/function/Failable.html

public List<Class> getClasses() throws ClassNotFoundException {
    List<Class> classes =
            Stream.of("java.lang.Object", "java.lang.Integer", "java.lang.String")
                    .map(Failable.asFunction(Class::forName))
                    .collect(Collectors.toList());
    return classes;
}

其他回答

我写了一个扩展Stream API的库,以允许抛出受控异常。它使用了Brian Goetz的技巧。

您的代码将变成

public List<Class> getClasses() throws ClassNotFoundException {     
    Stream<String> classNames = 
        Stream.of("java.lang.Object", "java.lang.Integer", "java.lang.String");

    return ThrowingStream.of(classNames, ClassNotFoundException.class)
               .map(Class::forName)
               .collect(Collectors.toList());
}

这个答案类似于17,但避免了包装器异常定义:

List test = new ArrayList();
        try {
            test.forEach(obj -> {

                //let say some functionality throws an exception
                try {
                    throw new IOException("test");
                }
                catch(Exception e) {
                    throw new RuntimeException(e);
                }
            });
        }
        catch (RuntimeException re) {
            if(re.getCause() instanceof IOException) {
                //do your logic for catching checked
            }
            else 
                throw re; // it might be that there is real runtime exception
        }

我使用这种包装异常:

public class CheckedExceptionWrapper extends RuntimeException {
    ...
    public <T extends Exception> CheckedExceptionWrapper rethrow() throws T {
        throw (T) getCause();
    }
}

它需要静态处理这些异常:

void method() throws IOException, ServletException {
    try { 
        list.stream().forEach(object -> {
            ...
            throw new CheckedExceptionWrapper(e);
            ...            
        });
    } catch (CheckedExceptionWrapper e){
        e.<IOException>rethrow();
        e.<ServletExcepion>rethrow();
    }
}

在网上试试!

尽管在第一次rethrow()调用期间无论如何都会重新抛出异常(哦,Java泛型…),这种方式允许获得可能异常的严格静态定义(需要在抛出中声明它们)。不需要instanceof或其他东西。

你不能。

然而,你可能想要看看我的一个项目,它可以让你更容易地操纵这种“投掷lambda”。

在你的情况下,你可以这样做:

import static com.github.fge.lambdas.functions.Functions.wrap;

final ThrowingFunction<String, Class<?>> f = wrap(Class::forName);

List<Class> classes =
    Stream.of("java.lang.Object", "java.lang.Integer", "java.lang.String")
          .map(f.orThrow(MyException.class))
          .collect(Collectors.toList());

并捕获MyException。

这是一个例子。另一个例子是你可以. orreturn()一些默认值。

请注意,这仍然是一项正在进行的工作,更多的是来。更好的名字,更多的功能等等。

这样做是不安全的。你可以作弊,但你的程序坏了,这将不可避免地反过来伤害别人(应该是你,但我们的作弊经常会伤害到别人)。

这里有一个稍微安全一点的方法(但我仍然不推荐这样做)。

class WrappedException extends RuntimeException {
    Throwable cause;

    WrappedException(Throwable cause) { this.cause = cause; }
}

static WrappedException throwWrapped(Throwable t) {
    throw new WrappedException(t);
}

try 
    source.stream()
          .filter(e -> { ... try { ... } catch (IOException e) { throwWrapped(e); } ... })
          ...
}
catch (WrappedException w) {
    throw (IOException) w.cause;
}

在这里,您要做的是捕获lambda中的异常,从流管道中抛出一个信号,表明计算异常失败,捕获信号,并对该信号进行操作以抛出底层异常。关键是您总是捕获合成异常,而不是在没有声明抛出异常的情况下允许已检查异常泄漏。