如何不抛出一般指定的异常?
How to not throw a generically specified exception?
我创建了一个 "producer" 接口(分别与方法引用一起使用,以便轻松模拟单元测试):
@FunctionalInterface
public interface Factory<R, T, X extends Throwable> {
public R newInstanceFor(T t) throws X;
}
我是这样创建的,因为我的第一个用例实际上必须抛出一些已检查的 WhateverException
。
但是我的第二个用例没有 X 可以抛出。
我能想到的让编译器满意的最好办法是:
Factory<SomeResultClass, SomeParameterClass, RuntimeException> factory;
可以编译并满足我的需要,但仍然很丑陋。有没有办法保留单一接口,但在声明特定实例时不提供 X?
您不能在 Java 中这样做。唯一的办法就是创建一个子接口。
public interface DefaultExceptionFactory<R, T>
extends Factory<R, T, RuntimeException>
唯一的方法是子类化——但我打赌你知道这一点。为了使我的论点更有说服力,请查看扩展 BiFunction
.
的 BinaryOperator
是否必须使异常通用?为什么不定义接口为
@FunctionalInterface
public interface Factory<R, T> {
public R newInstanceFor(T t) throws Throwable;
}
如果需要,您始终可以在调用函数中捕获异常并检查类型。
如果可能的话,您可以像下面的代码一样将方法定义为通用的:
@FunctionalInterface
public interface Factory<R, T> {
public <X extends Throwable> R newInstanceFor(T t) throws X;
}
这更像是一个 "social engineering" 答案:我们在 lambda 形式上放置了一个合同,它不会抛出任何东西:
public interface Factory<T, R, X> {
public R newInstanceFor(T arg) throws X;
public static Factory<R, U, AssertionError> neverThrows(Factory<U, V, ?> input) {
return u -> {
try {
return input.newInstanceFor(u);
}
catch(Throwable t) {
throw new AssertionError("Broken contract: exception thrown", t);
}
};
}
}
用法是这样的,或者类似于:
class MyClass {
Factory<MyInput, MyOtherClass, AssertionError> factory;
MyClass(Factory<MyInput, MyOtherClass, ?> factory) {
this.factory = Factory.neverThrows(factory);
}
public void do() {
factory.newInstanceFor(new MyInput()).do();
}
}
这种方法的缺点:你不能在类型签名中真正指定契约,契约是一个实现细节。如果你想在类型签名中使用它,你将需要第二个子接口。
您可以使用 Project Lombok 的 @SneakyThrows 注释:
@FunctionalInterface
public interface Factory<R, T> {
@SneakyThrows
R newInstanceFor(T t);
}
这允许您抛出任何异常(已检查或未检查)。但是请阅读文档,因为必须小心处理此功能。
我创建了一个 "producer" 接口(分别与方法引用一起使用,以便轻松模拟单元测试):
@FunctionalInterface
public interface Factory<R, T, X extends Throwable> {
public R newInstanceFor(T t) throws X;
}
我是这样创建的,因为我的第一个用例实际上必须抛出一些已检查的 WhateverException
。
但是我的第二个用例没有 X 可以抛出。
我能想到的让编译器满意的最好办法是:
Factory<SomeResultClass, SomeParameterClass, RuntimeException> factory;
可以编译并满足我的需要,但仍然很丑陋。有没有办法保留单一接口,但在声明特定实例时不提供 X?
您不能在 Java 中这样做。唯一的办法就是创建一个子接口。
public interface DefaultExceptionFactory<R, T>
extends Factory<R, T, RuntimeException>
唯一的方法是子类化——但我打赌你知道这一点。为了使我的论点更有说服力,请查看扩展 BiFunction
.
BinaryOperator
是否必须使异常通用?为什么不定义接口为
@FunctionalInterface
public interface Factory<R, T> {
public R newInstanceFor(T t) throws Throwable;
}
如果需要,您始终可以在调用函数中捕获异常并检查类型。
如果可能的话,您可以像下面的代码一样将方法定义为通用的:
@FunctionalInterface
public interface Factory<R, T> {
public <X extends Throwable> R newInstanceFor(T t) throws X;
}
这更像是一个 "social engineering" 答案:我们在 lambda 形式上放置了一个合同,它不会抛出任何东西:
public interface Factory<T, R, X> {
public R newInstanceFor(T arg) throws X;
public static Factory<R, U, AssertionError> neverThrows(Factory<U, V, ?> input) {
return u -> {
try {
return input.newInstanceFor(u);
}
catch(Throwable t) {
throw new AssertionError("Broken contract: exception thrown", t);
}
};
}
}
用法是这样的,或者类似于:
class MyClass {
Factory<MyInput, MyOtherClass, AssertionError> factory;
MyClass(Factory<MyInput, MyOtherClass, ?> factory) {
this.factory = Factory.neverThrows(factory);
}
public void do() {
factory.newInstanceFor(new MyInput()).do();
}
}
这种方法的缺点:你不能在类型签名中真正指定契约,契约是一个实现细节。如果你想在类型签名中使用它,你将需要第二个子接口。
您可以使用 Project Lombok 的 @SneakyThrows 注释:
@FunctionalInterface
public interface Factory<R, T> {
@SneakyThrows
R newInstanceFor(T t);
}
这允许您抛出任何异常(已检查或未检查)。但是请阅读文档,因为必须小心处理此功能。