如何不抛出一般指定的异常?

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);
}

这允许您抛出任何异常(已检查或未检查)。但是请阅读文档,因为必须小心处理此功能。