我提出了以下代码,以便能够自动将已检查的异常转换为运行时异常的消费者传递。
这个想法是消费者中的代码可以抛出一个已检查的异常,如果它这样做,那么它将被转换为运行时异常。
接口:
@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上编译它,也许这有帮助。
答案 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
,并且该接口可以处理异常。