Consumer <t>将已检查的异常转换为未经检查的异常引用问题</t>

时间:2014-04-09 12:38:08

标签: java exception java-8

我提出了以下代码,以便能够自动将已检查的异常转换为运行时异常的消费者传递。

这个想法是消费者中的代码可以抛出一个已检查的异常,如果它这样做,那么它将被转换为运行时异常。

接口:

@FunctionalInterface
interface WrappingConsumer<T> extends Consumer<T> {
    @Override
    default void accept(final T t) {
        toRuntimeException(() -> wrappingAccept(t));
    }

    void wrappingAccept(final T t) throws Exception;

    static void toRuntimeException(final ExceptionRunnable exceptionRunnable) {
        Objects.requireNonNull(exceptionRunnable);
        try {
            exceptionRunnable.run();
        } catch (Exception ex) {
            throw new RuntimeException(ex);
        }
    }
}

@FunctionalInterface
interface ExceptionRunnable {
    abstract void run() throws Exception;
}

测试类:

public class WrappingConsumerProject {
    public static void main(String[] args) {
        WrappingConsumer<Integer> wrappingConsumer = WrappingConsumerProject::test; //Works
        Consumer<Integer> consumer = wrappingConsumer;  //Works

        testConsume(wrappingConsumer);  //Works
        testConsume(consumer);  //Works

        testConsume(WrappingConsumerProject::test); //Fails
        testConsume((Consumer<Integer>)WrappingConsumerProject::test);  //Fails
    }

    private static void test(Integer integer) throws IOException { }

    private static void testConsume(Consumer<Integer> consumer) { }
}

有些版本可以正常工作,而其他版本会出现编译错误吗?

我正在Netbeans 8.0上编译它,也许这有帮助。

3 个答案:

答案 0 :(得分:2)

我有根据的猜测:testConsume需要Consumer<Integer>参数:

private static void testConsume(Consumer<Integer> consumer) { }

所以当编译器看到这个时:

testConsume(WrappingConsumerProject::test); //Fails

它当然不能告诉您要将方法引用解释为WrappingConsumer<Integer>,并且您没有告诉编译器任何可以帮助它相信这应该是WrappingConsumer<Integer>的东西}。 (显然,查看名称WrappingConsumerProject是不够的。:-)所以它必须尝试将WrappingConsumerProject::test解释为Consumer<Integer>,这意味着它等同于

testConsume(new Consumer<Integer> () {
    public void accept(Integer t) {
        WrappingConsumerProject.test(t);
    }
}

失败,因为未处理IOException。另一方面,如果知道接口是WrappingConsumer<Integer>,它将正确处理它,因为它会将方法接口解释为{{1}的实现的一部分}而不是wrappingAccept()。因此:

accept()

相当于

WrappingConsumer<Integer> wrappingConsumer = WrappingConsumerProject::test;

这是正常的,因为WrappingConsumer<Integer> wrappingConsumer = new WrappingConsumerProject<Integer>() { public void wrappingAccept(Integer t) throws Exception { WrappingConsumerProject.test(t); } } 有一个wrappingAccept()子句。当这个新对象被视为throws时,它仍然可以,因为当调用Consumer<Integer>方法时:

accept()

它会调用您设置的Consumer<Integer> consumer = wrappingConsumer; // then somebody calls consumer.accept(); ;它不会直接调用default。宣布WrappingConsumerProject.test时已经确定了这个事实。将其投射到wrappingConsumer并不会改变这一事实。

但事实并非如此,比如说

Consumer<Integer>

Consumer<Integer> consumer2 = WrappingConsumerProject::test;

在这些情况下,必须假设testConsume(WrappingConsumerProject::test); //Fails WrappingConsumerProject::test的实现,因为它必须将其解释为accept()。如上所述,没有什么能告诉它将其解释为Consumer<Integer>。因此,如果有人打电话

WrappingConsumer<Integer>

尝试直接拨打consumer2.accept(); ,而无需通过WrappingConsumerProject::test

无论如何,虽然我不是关于类型推断或其他JLS规则细微差别的专家,但这对我来说很有意义。

答案 1 :(得分:0)

Java 8中相应的jep为http://openjdk.java.net/jeps/101

在我的机器上,该示例直接从命令行工作而没有错误。

$ java -version
java version "1.8.0"
Java(TM) SE Runtime Environment (build 1.8.0-b129)
Java HotSpot(TM) Server VM (build 25.0-b69, mixed mode)

在Eclipse Luna中集成它不会编译。 所以我的猜测是,现在这是一个工具问题,随着时间的推移会逐渐消失。

编辑:完全重写答案。

答案 2 :(得分:0)

有趣的问题。我在Eclipse Kepler中编译。这是我的结果:

    testConsume(wrappingConsumer);  //Works
    testConsume(consumer);  //Works

    testConsume(WrappingConsumerProject::test); //Fails
    testConsume((Consumer<Integer>)WrappingConsumerProject::test);  //Fails

然而:

    testConsume((WrappingConsumer<Integer>)WrappingConsumerProject::test); //Works

当它失败时,它失败了Unhandled Exception type IOException。原因是您的testConsume方法期望使用Consumer接口。当您提供方法参考WrappingConsumerProject::test时,这将履行Consumer<Integer>合同。编译器不知道WrappingConsumer接口,因为期望的类型是Consumer而不是WrappingConsumer

添加强制转换使编译器知道它应该用于方法引用的接口是WrappingConsumer,并且该接口可以处理异常。