【问题标题】:Convert Runnable to Supplier将 Runnable 转换为供应商
【发布时间】:2019-02-10 15:39:31
【问题描述】:

如何将Runnable 转换为Supplier

public <T> T useSupplier(Supplier<T> supplier) {
    // Does something with supplier and returns supplied value
    ...
    return value;
}

public void useRunnable(Runnable runnable) {
    // Somehow convert runnable to Supplier
    ...
    useSupplier(supplier);
}

这里我想为useRunnable重用方法useSupplier,例如因为我不想复制代码。 useSupplier 的行为与这个问题无关,假设它包装了抛出的异常,或者在同步块中使用供应商。


编辑:澄清一下,useSupplier 方法不与提供的值交互,它只是返回它。 useSupplier 的功能是在某些情况下从供应商那里检索值,在我的情况下,它捕获(特定)RuntimeExceptions,创建一个新异常作为原因并抛出它:

public <T> T useSupplier(Supplier<T> supplier) {
    try {
        return supplier.get();
    }
    catch (RuntimeException runtimeException) {
        throw new MyException("Supplier threw exception", runtimeException);
    }
}

以下解决方案不起作用(在 Java 8 中):

useSupplier(runnable);
useSupplier(runnable::run);
useSupplier((Supplier<Void>) runnable::run);

我能想到的一个解决方案是创建一个新的Supplier,它返回一个任意值:

useSupplier(() -> {
    runnable.run();
    return null;
});

有没有更小的解决方案?

编辑:正如 Holger 在 cmets 中指出的那样,使用 runnable::run 也会创建新的 lambda 实例,因为它是有状态的,另请参阅 this answer

【问题讨论】:

  • run 方法返回 void,因此您无法提供任何值。
  • 对于useRunnable我不关心返回值。正如我所写,useSupplier 在同步块中使用了例如供应商,这也是我想要为useRunnable 提供的。
  • 考虑使用返回值的Callable,而不是Runnable
  • 你能告诉我们更多关于useRunnable(...)useSupplier(...)的语义吗?从命名来看,它表明useRunnable(...) 产生了一个新线程(至少对我而言)。
  • @Turing85 我正在捕获(特定的)RuntimeExceptions 并以它们作为原因抛出新异常。所以我不关心供应商返回的价值,我只返回它。

标签: java java-8 functional-interface


【解决方案1】:

在您的情况下,您无法避免创建新对象。即使某处有将 Runnable 转换为 Supplier 的方法,它也会在那里创建一个对象。 所以你的解决方案是有效的,你不会找到更好的。

请注意,Supplier 应该提供值,而 Runnable 只代表一个动作。它们用于不同的目的。因此,您需要将 Runnable 转换为 Supplier 可能是涉及设计问题的结果。

【讨论】:

    【解决方案2】:

    查看您的设计,您可能只是在寻找Consumer,它接受一个类型并仅处理(使用)它而不返回值,并且也可用于适应可运行对象,而不是 Supplier另一方面,预计会返回(提供)一个值后处理。

    你可以使用类似的东西:

    private static <T> void useConsumer(Consumer<T> consumer, T val) {
        // Does something with supplier and returns supplied value
        consumer.accept(val);
    }
    
    public static <T> void useRunnable(Runnable runnable) {
        useConsumer(Runnable::run, runnable);
    }
    

    如果要求肯定使用Supplier,那么您可以调用该方法:

    public static void useRunnable(Runnable runnable) {
        useSupplier(() -> runnable); // the useSupplier returns the 'runnable' when this method is called
    }
    

    正如 cmets 中提到的,现在当您调用 useRunnable 时,useSupplier 将返回相同的 runnable,但方法 useRunnable 再次是 void,因此它完全被忽略了。

    【讨论】:

    • 我想要一个以Supplier 为参数的方法。我现在编辑了我的问题,希望能更清楚地表明我不想处理提供的值。
    • 不,runnable 应该运行。这样(通过我对我的问题所做的编辑)供应商将提供将被丢弃的可运行文件。
    • @Marcono1234 供应商会提供刚刚被丢弃的runnable,那为什么还要使用Supplier呢?这就是为什么我最初建议改用Consumer。请注意,您的实现() -&gt; { runnable.run(); return null; } 做了return 某事,无论是null,但它与runnable 没有任何关系。还是这样?
    • 查看我对问题的编辑。当我使用供应商时,我对价值感兴趣,但希望以某种方式处理所有异常。当我使用 runnable 时,我还希望以某种方式处理所有异常。
    • @Marcono1234 然后我怀疑没有比() -&gt; { runnable.run(); return null; } 更好的称呼了。当您不关心useSupplierreturn 类型并且主要对处理来自runnable 执行的异常感兴趣时。
    【解决方案3】:

    如果您发现自己在代码库中经常使用这种模式,则可能值得创建一个 RunnableSupplier 类:

    public class RunnableSupplier<T> implements Supplier<T> {
        private final Runnable runnable;
    
        public RunnableSupplier(Runnable runnable) {
            this.runnable = runnable;
        }
    
        @Override
        public T get() {
            runnable.run();
            return null;
        }
    }
    
    public void useRunnable(Runnable runnable) {
        useSupplier(new RunnableSupplier(runnable));
    }
    

    我已将此类设为通用,因为可以为所有通用类型的供应商返回 null。这使得可以在需要特定类型的Suppliers 的库方法中使用它们,只要它们允许空结果。如果您想强制它始终为Supplier&lt;Void&gt;,则可以直接将其设为非泛型并实现Supplier&lt;Void&gt; 而不是Supplier&lt;T&gt;

    【讨论】:

      猜你喜欢
      • 2019-03-07
      • 1970-01-01
      • 2011-03-05
      • 1970-01-01
      • 2019-06-20
      • 1970-01-01
      • 2021-07-17
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多