当通用方法返回“nothing”且无法返回null时,应该返回什么?

7
假设我有一个类似这样的库方法(非常简略):
public static <V> Optional<V> doSomethingWith(Callable<V> callable) {
    try {
        return Optional.of(callable.call());
    } catch (Exception ex) {
        // Do something with ex
        return Optional.empty();
    }
}

我想要做一些不返回值的事情,比如:
    Library.</*What1*/>doSomethingWith(() -> {
        foo();
        return /*what2*/;
    });

我的首要反应是将类型设为Void并返回null,但由于结果被包裹在Optional中,这会引发异常。
有没有一些合理的占位符来代替/*What1*//*what2*/,使它们看起来不像是完全随机的,例如Integer0
[编辑] 我试图避免使用Optional.ofNullable,因为这里的空值表示callable.call()未正常完成。

3
你能否将库方法更改为使用 Optional.ofNullable?如果它总是存在,那么返回可选项有什么意义?(注意:已经尽量保持原意并使语言更加通俗易懂) - Tagir Valeev
1
Optional是一个容器对象,它可能包含或不包含非空值。那么问题是什么? - user207421
1
@MarkJeronimus 如果您无法修改库,则似乎您无法合法地进行此操作,除非使用不安全的强制转换等丑陋的黑客技巧。您是否可以考虑将可调用对象的类型更改为其他类型(例如 Callable<Boolean> 并返回 Boolean.TRUE)? - Tagir Valeev
2
可能是Java泛型void/Void类型的重复问题。 - Joe
2
你也可以创建一个自定义枚举(例如 Result 或其他名称),其中只有一个常量(例如 EMPTY 或其他名称)。 - MC Emperor
显示剩余4条评论
4个回答

6
如果需要为永远不会使用的泛型参数提供类型提示,则可以使用 Void,JDK 在某些情况下也会这样做,例如在将 Runnable 转换为 CompletableFuture<T> 时,它会为 T 使用 Void。
如果使用 Optional.ofNullable,则可以只返回 null 给 what2,这是 Void 的唯一有效值。
如果您想避免使用 Optional.ofNullable,因为它在此处表示 callable.call() 没有正常完成,则您正在使用错误的工具。正确的语义应该使用 CompletionStageCompletableFuture

4
我通常使用Boolean.TRUE标记成功,但您也可以返回Void.class。从成本的角度来看,两者都很便宜,因为不是每次返回都会创建一个要丢弃的新对象。虽然Class<Void>不仅仅是Void,但它同样可以用作将某些内容标记为void
正如已经提到的,您还可以创建自己的Result类/枚举。
或者您当然也可以返回Optional.<Void>nothing()。这将导致一些Optional<Optional<Void>>,但也能达到目的。
如果您认为以上所有方法都很丑陋,我担心API可能不太适合您的需求。请提出问题/请求或寻找其他替代品。

我最喜欢枚举选项。特别是因为昨天我正在思考为什么没有内置(非原始)类型恰好有两种可能性。相比之下,Void 有一种可能性:null,而 Boolean 有三种可能性:nullFALSETRUE。(当然,Object 有无限的可能性。) - Mark Jeronimus

1

你也可以创建类似于 Void 的自定义类型

public class Result {
     public static final Result OK = new Result();

     private Result(){}
}

然后返回 Result.OK
如果需要,您还可以扩展此类型以表示错误。
但是,如果您不需要任何特殊功能,可能使用java的Void更可取。

我会使用枚举来解决这个问题。 - TheConstructor
这取决于你要做什么,使用 singleton 无法管理错误,因为每次都需要一个具有不同消息的不同实例。 - rascio
枚举继承了一些更多的方法(比如 toString),这就是为什么我通常更喜欢将它们用于类似建议中的 Result.OK 这样的信号实例。如果 API 中使用了 ResultOrError 类而不是 Optional,那么 OP 的问题就不存在了。 - TheConstructor

0

使用Void作为返回类型是逻辑上的选择,因为它表示“无”,但实际上会返回一个Void实例。

尽管Void的javadoc说它是:

...一个不可实例化的占位符类...

你仍然可以实例化它:

try {
    Constructor<Void> c = Void.class.getDeclaredConstructor();
    c.setAccessible(true);
    return c.newInstance();
} catch (Exception perfunctory) {
    return null; // won't happen
}

8
错误的建议。当你通过黑客手段创建一个 Void 实例时,你违反了多项合同,更不用说未来/备选 JDK 中可能出现的实现更改会导致 不会发生 的情况真正发生。 - Mark Jeronimus
@标记,具体来说,哪些合同被违反了?请提供链接等。我坚信没有这样的合同,如果将来的版本更改构造函数以使此代码无法工作(非常不可能),那时再担心吧。 - Bohemian

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接