【问题标题】:What to return when generic methods returns 'nothing' but null can't be returned?当泛型方法返回'nothing'但不能返回null时返回什么?
【发布时间】:2016-12-28 10:55:19
【问题描述】:

假设我有一个这样的库方法(非常简略):

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() 没有正常完成。

【问题讨论】:

  • 你不能改变库方法来使用Optional.ofNullable吗?如果它总是存在,那么返回可选的意义何在?
  • @MarkJeronimus 如果您不能修改库,似乎如果没有像未经检查的演员这样的丑陋黑客,您就无法合法地做到这一点。您能否考虑将可调用对象的类型更改为其他类型(例如 Callable&lt;Boolean&gt; 并返回 Boolean.TRUE)?
  • 您还可以创建一个自定义枚举(如Result 或其他),其中只有一个常量(如EMPTY 或其他)。

标签: java generics java-8 optional


【解决方案1】:

如果您需要一个永远不会使用的泛型参数的类型提示,您可以使用Void,JDK 在某些情况下也会这样做,例如在将 Runnable 转换为 CompletableFuture&lt;T&gt; 时,它使用 Void 作为 T。

如果您使用Optional.ofNullable,那么您可以只为what2 返回null,这是Void 的唯一有效值。

[edit] 我试图避免 Optional.ofNullable 因为这里使用空表示 callable.call() 没有正常完成。

那么你在工作中使用了错误的工具。 CompletionStageCompletableFuture 具有正确的语义。

【讨论】:

    【解决方案2】:

    我通常使用Boolean.TRUE 来标记成功,但您也可以返回Void.class。两者都很便宜,因为并非每次返回都会创建一个要丢弃的新对象。尽管Class&lt;Void&gt; 不仅仅是Void,它也可以用于将某些东西标记为void

    如前所述,您还可以创建自己的 Result-class/-enum。

    或者你当然也可以返回Optional.&lt;Void&gt;nothing()。这将导致一些Optional&lt;Optional&lt;Void&gt;&gt;,但也可以做到这一点。

    如果您认为以上所有内容都很难看,我担心 API 可能无法很好地满足您的需求。提出问题/拉取请求或寻找其他内容。

    【讨论】:

    • 我最喜欢枚举选项。特别是因为昨天我只是想知道为什么没有内置(非原始)类型恰好有两种可能性。比较:Void 有一种可能性:null,而Boolean 有三种可能性:nullFALSETRUE。 (Object当然有无限可能。)
    【解决方案3】:

    您也可以创建自己的类型,类似于 Void

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

    然后返回Result.OK

    如果需要,您还可以增强此类型以表示错误。

    但如果您不需要任何特别的东西,也许使用 java Void 更好。

    【讨论】:

    • 我会为此使用枚举。
    • 这取决于您必须做什么,使用单例您无法管理错误,因为使用它您每次都需要不同的实例和不同的消息。
    • Enums 继承了更多的方法(例如toString),为什么我通常更喜欢将它们用于像建议的Result.OK 这样的信令实例。如果在 API 中使用了一些 ResultOrError-class 而不是 Optional,则 OP 的问题将不存在。
    【解决方案4】:

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

    虽然javadoc for Void 说是:

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

    你仍然可以实例化它:

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

    【讨论】:

    • 不好的建议。当您将Void 的实例破解为存在时,您会破坏多个合同,更不用说未来/替代JDK 中可能导致won't happen 发生的实现更改。
    • @mark 哪些合同具体被破坏了?请提供链接等。我坚持认为没有这样的合同,如果未来的版本改变了构造函数使得这段代码不起作用(非常不可能),那么请担心。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2021-12-13
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多