Java的8:强制检查的异常的lambda表达式处理。 为什么强制性的,不是可有可无的?(Jav

2019-07-17 14:16发布

我打在Java 8.0中的新功能的λ,发现在Java 8提供的做法真的很有用。 但是,我不知道还有什么能让一个变通为以下情形的方法。 假设你有需要某种工厂来填补对象池,例如(使用对象池包装java.lang.functions.Factory ):

public class JdbcConnectionPool extends ObjectPool<Connection> {

    public ConnectionPool(int maxConnections, String url) {
        super(new Factory<Connection>() {
            @Override
            public Connection make() {
                try {
                    return DriverManager.getConnection(url);
                } catch ( SQLException ex ) {
                    throw new RuntimeException(ex);
                }
            }
        }, maxConnections);
    }

}

将所述功能接口入λ表达后,上面的代码变成这样:

public class JdbcConnectionPool extends ObjectPool<Connection> {

    public ConnectionPool(int maxConnections, String url) {
        super(() -> {
            try {
                return DriverManager.getConnection(url);
            } catch ( SQLException ex ) {
                throw new RuntimeException(ex);
            }
        }, maxConnections);
    }

}

没那么糟了,但检查的异常java.sql.SQLException需要try / catch的拉姆达内部块。 在我的公司,我们用了很长一段时间两个接口:

  • IOut<T>是一个相当于java.lang.functions.Factory ;
  • 并为通常需要检查的异常传播的情况下,一种特殊的接口: interface IUnsafeOut<T, E extends Throwable> { T out() throws E; } interface IUnsafeOut<T, E extends Throwable> { T out() throws E; }

IOut<T>IUnsafeOut<T>应该迁移到Java 8期间被除去,但是不存在用于精确匹配IUnsafeOut<T, E> 如果lambda表达式可以处理checked异常,如他们是不加以控制,它可能是能够使用简单的如上面的构造函数如下:

super(() -> DriverManager.getConnection(url), maxConnections);

这看起来干净多了。 我看到,我可以改写ObjectPool超类来接受我们的IUnsafeOut<T>但据我所知,Java的8还没有完成,因此可能会出现一些变化,如:

  • 实施类似的东西IUnsafeOut<T, E> (说实话,我认为肮脏-主体必须选择接受什么:无论是Factory还是“不安全工厂”不能有兼容的方法签名)
  • 在根本无视检查了lambda表达式的异常,因此无需IUnsafeOut<T, E>的代理人? (为什么不是如另一个重要的变化:OpenJDK的,我使用, javac现在不需要变量和参数声明为final在一个匿名类[功能接口]或lambda表达式将被捕获)

所以,问题一般是:有没有办法绕过lambda表达式checked异常,或者它在未来的计划中,直到8 Java的最后释放?


更新1

HM-毫米,据我了解我们目前有,似乎是没有办法的那一刻,尽管引用的文章是从2010年日期: 作者Brian Goetz解释了Java异常的透明度 。 如果没有在Java中8太大的变化,这也算是一个答案。 此外布赖恩说, interface ExceptionalCallable<V, E extends Exception> (我提到IUnsafeOut<T, E extends Throwable>我们的代码遗留下来的)几乎是没用的,我同意他的观点。

难道我仍然想念别的东西吗?

Answer 1:

不知道我真的回答你的问题,但不能单纯使用类似的东西?

public final class SupplierUtils {
    private SupplierUtils() {
    }

    public static <T> Supplier<T> wrap(Callable<T> callable) {
        return () -> {
            try {
                return callable.call();
            }
            catch (RuntimeException e) {
                throw e;
            }
            catch (Exception e) {
                throw new RuntimeException(e);
            }
        };
    }
}

public class JdbcConnectionPool extends ObjectPool<Connection> {

    public JdbcConnectionPool(int maxConnections, String url) {
        super(SupplierUtils.wrap(() -> DriverManager.getConnection(url)), maxConnections);
    }
}


Answer 2:

在拉姆达邮件列表这是throughly讨论 。 正如你所看到的作者Brian Goetz认为那里的替代方法是写自己的组合子:

或者你可以写自己的平凡组合子:

 static<T> Supplier<T> exceptionWrappingSupplier(Supplier<T> b) { return e -> { try { b.accept(e); } catch (Exception e) { throw new RuntimeException(e); } }; } 

你可以写一次,在不到它花时间来写你的原始电子邮件。 而同样曾经为每种SAM的使用。

我宁愿我们就来看看这是“玻璃99%满”,而不是替代。 并非所有的问题都需要新的语言功能的解决方案。 (且不说新的语言功能总是会引起新的问题。)

在那些日子里,用户界面被称为块。

我想,这相当于用JB Nizet的答案 。

后来布莱恩解释了为什么这是这样设计(问题的原因)

是的,你必须提供自己的特殊的地对空导弹。 但随后的λ转换将正常工作与他们。

该EG讨论额外的语言和库支持,对于这个问题,并最终认为这是一个坏的成本/收益权衡。

基于库的解决方案,导致SAM类型的2倍爆炸(特殊VS没有),这与原始的专业化现有组合爆炸冲突很严重。

可用的基于语言的解决方案是从复杂性/价值权衡的输家。 虽然有,我们将继续探索一些替代方案 - 但显然不是8可能不是9任。

在此期间,你有工具,做你想做什么。 我得到你喜欢我们提供最后一英里你(和,其次,你的要求是真正的“你为什么不干脆放弃对检查的异常已经是”一个自动精简含蓄的要求),但我认为目前的状态让你得到你的工作完成。



Answer 3:

2015年9月:

您可以使用ET这一点。 ET是一种小型的Java 8库异常转换/翻译。

随着ET你可以写:

super(() -> et.withReturningTranslation(() -> DriverManager.getConnection(url)), maxConnections);

多行版本:

super(() -> {
  return et.withReturningTranslation(() -> DriverManager.getConnection(url));
}, maxConnections);

所有你需要做之前,正在创造一个新的ExceptionTranslator实例:

ExceptionTranslator et = ET.newConfiguration().done();

这个实例是线程安全的可以由多个部件共享。 您可以配置更具体的异常转换规则(例如FooCheckedException -> BarRuntimeException )如果你喜欢。 如果没有其他规则可用,checked异常将自动转换为RuntimeException

(免责声明:我是ET的作者)



Answer 4:

你有没有考虑使用一个RuntimeException(未选中)包装类走私原始异常出lambda表达式,然后浇注包装的异常回到它原来的检查异常?

class WrappedSqlException extends RuntimeException {
    static final long serialVersionUID = 20130808044800000L;
    public WrappedSqlException(SQLException cause) { super(cause); }
    public SQLException getSqlException() { return (SQLException) getCause(); }
}

public ConnectionPool(int maxConnections, String url) throws SQLException {
    try {
        super(() -> {
            try {
                return DriverManager.getConnection(url);
            } catch ( SQLException ex ) {
                throw new WrappedSqlException(ex);
            }
        }, maxConnections);
    } catch (WrappedSqlException wse) {
        throw wse.getSqlException();
    }
}

创建自己的独特的类应防止误认为您的拉姆达里面包裹着彼此未经检查的异常的可能性任,即使异常某处管线你赶上并重新抛出之前序列化。

嗯...我看到这是一个问题在这里的唯一的事情是,你是在构造函数中有()调用超级其根据法律,必须在你的构造函数的第一个语句这样做。 是否try算作一个先前的发言? 我有我自己的代码这方面的工作(不包括构造函数)。



Answer 5:

我们在我公司与这有助于我们开发了一个内部项目。 我们决定在两个月前上市。

这是我们想出了:

@FunctionalInterface
public interface ThrowingFunction<T,R,E extends Throwable> {
R apply(T arg) throws E;

/**
 * @param <T> type
 * @param <E> checked exception
 * @return a function that accepts one argument and returns it as a value.
 */
static <T, E extends Exception> ThrowingFunction<T, T, E> identity() {
    return t -> t;
}

/**
 * @return a Function that returns the result of the given function as an Optional instance.
 * In case of a failure, empty Optional is returned
 */
static <T, R, E extends Exception> Function<T, Optional<R>> lifted(ThrowingFunction<T, R, E> f) {
    Objects.requireNonNull(f);

    return f.lift();
}

static <T, R, E extends Exception> Function<T, R> unchecked(ThrowingFunction<T, R, E> f) {
    Objects.requireNonNull(f);

    return f.uncheck();
}

default <V> ThrowingFunction<V, R, E> compose(final ThrowingFunction<? super V, ? extends T, E> before) {
    Objects.requireNonNull(before);

    return (V v) -> apply(before.apply(v));
}

default <V> ThrowingFunction<T, V, E> andThen(final ThrowingFunction<? super R, ? extends V, E> after) {
    Objects.requireNonNull(after);

    return (T t) -> after.apply(apply(t));
}

/**
 * @return a Function that returns the result as an Optional instance. In case of a failure, empty Optional is
 * returned
 */
default Function<T, Optional<R>> lift() {
    return t -> {
        try {
            return Optional.of(apply(t));
        } catch (Throwable e) {
            return Optional.empty();
        }
    };
}

/**
 * @return a new Function instance which wraps thrown checked exception instance into a RuntimeException
 */
default Function<T, R> uncheck() {
    return t -> {
        try {
            return apply(t);
        } catch (final Throwable e) {
            throw new WrappedException(e);
        }
    };
}

}

https://github.com/TouK/ThrowingFunction/



Answer 6:

包装的描述方式不工作异常。 我尝试过了,我仍然得到编译器错误,这实际上是根据规格:lambda表达式抛出异常是与所述方法的参数的目标类型不相容:可赎回; 调用()不把它所以无法通过lambda表达式作为可赎回。

所以基本上不存在解决方法:我们坚持写作的样板。 我们唯一能做的事情就是表达我们的意见,这需要固定。 我认为规范不应该只是一味地抛弃基于不兼容引发的异常目标类型:应该随后检查抛出的异常不兼容是否被捕获或声明为调用范围罚球。 而对于未内联我建议,我们可以将其标记为默默地抛出检查型异常(沉默在某种意义上说,编译器不应该检查,但运行时还是应该赶上)lambda表达式。 让我们纪念那些=>而不是 - >我知道这不是一个讨论的网站,但由于这是这个问题的唯一解决办法,让自己听到,让我们改变这个天赋!



Answer 7:

Paguro提供包裹检查的异常功能接口 。 我开始做这个工作,你问你的问题后几个月,所以你很可能是灵感它的一部分!

你会发现,只有4个在Paguro功能接口与包含在Java 8这是因为Paguro喜欢泛型元的43个接口。

Paguro内建到它的不可变的集合(从Clojure的复制)单次转换。 这些变换大致相当于Clojure的换能器或Java 8流,但他们接受检查包裹的异常功能接口。 请参阅: Paguro和Java 8流之间的差异 。



Answer 8:

可以从您的lambda表达式扔了,他们只需要被宣布为“用自己的方式”(这让他们遗憾的是,没有可重复使用的标准JDK的代码,但是,嘿,我们做什么,我们罐)。

@FunctionalInterface
public interface SupplierIOException {
   MyClass get() throws IOException;
}

或者更一般的,美化版版本:

public interface ThrowingSupplier<T, E extends Exception> {
  T get() throws E;
}

REF 这里 。 也有使用“sneakyThrow”不申报checked异常的一提,但后来还是把他们。 这是伤害了我的脑袋一下,也许一个选项。



文章来源: Java 8: Mandatory checked exceptions handling in lambda expressions. Why mandatory, not optional?