我正在尝试理解当我应该考虑使用BooleanSupplier时的用例场景。在它的解释中的大多数例子中,我得到了最多的一个。我想知道使用BooleanSupplier比简单的比较有什么优势?
String s1 = "ABC";
String s2 = "ABC";
BooleanSupplier stringEquals = () -> s1.equals(s2);
System.out.println(stringEquals.getAsBoolean());与此相反的是-
System.out.println(s1.equals(s2));发布于 2018-01-20 20:27:32
理论上,我通常使用Suppliers的主要原因是,正如@Eugene在his answer中所说的那样,延迟执行。然而,在实践中,我从来没有任何理由特别使用BooleanSupplier。更重要的是,我发现很难想出一个真实的使用场景……
尽管如此,我认为展示一下Supplier<String>的典型用法可能是值得的。我希望这能在总体上对供应商的使用有所帮助。
典型的例子就是日志记录。假设您必须记录一个非常昂贵的计算的结果,该计算返回一个值,但仅当log level设置为DEBUG时。假设这个非常昂贵的计算由方法veryExpensive()表示,该方法返回一个值(就本例而言,返回类型并不重要)。
传统的使用模式是使用if语句,所以我们只在启用DEBUG日志级别时执行非常昂贵的计算:
if (logger.isDebugEnabled()) {
logger.debug("veryExpensive() returned: " + veryExpensive());
}这是预期的,因为如果日志级别设置为INFO,我们将永远不会调用veryExpensive()。但是现在假设你在你的代码中重复了同样的模式。很不错,是吧?一个简单的任务,如日志记录,已经用if语句污染了您的所有代码...(这不是我发明的,我已经见过这种模式很多次了)。
现在考虑一下如果logger.debug接受一个Supplier<String>而不是一个普通的String值会发生什么。在本例中,我们将不再需要if语句,因为将String值提取到日志中的逻辑现在驻留在logger.debug方法的实现中。现在的使用模式是:
logger.debug(() -> "veryExpensive() returned: " + veryExpensive());其中() -> "veryExpensive() returned: " + veryExpensive()是一个Supplier<String>。
这非常有效,因为veryExpensive()的执行会被推迟,直到logger.debug方法需要实际记录Supplier<String>返回的String,而这种情况只有在启用了DEBUG日志级别时才会发生。
发布于 2018-01-20 15:31:45
这只是Supplier的专门化,因此您应该真正地问一下为什么需要Supplier,而您的非常简单的示例并没有显示出对它的需求。在我看来,至少有两个原因需要这样做:
首先,是推迟执行。想想Optional.orElseGet(Supplier...) vs Optional.get()吧。您可能认为它们是相同的,但第一个只在需要时才执行。现在考虑这样一种情况,当返回的对象计算成本很高时(例如,一系列DB调用),您会选择哪一个?可以说,它应该是通过提供的Supplier实现的orElseGet。
第二个是通过对supply的调用一直产生一个对象,我的意思是这是Supplier的名字,以提供值。在Stream API中,当你需要返回一个新的对象时,比如在并行合并元素时,每个线程都会有自己的对象持有者。有关示例,请参阅:
public static<T, R> Collector<T, R, R> of(
Supplier<R> supplier,
BiConsumer<R, T> accumulator,
BinaryOperator<R> combiner...看看自定义Collector.of如何将第一个参数作为Supplier,在Collectors.toList (查看实现)或Collectors.toMap等中也会发生同样的事情。
我们在生产中使用它的一个例子是向调用者返回一个Supplier<Something>而不是Something。当调用者调用get时,我可以自由地做我想要返回Something的任何事情-例如,我们通常缓存对象。
发布于 2018-01-20 16:36:57
使用函数接口(这里是BooleanSupplier)而不是使用硬编码的基本思想是information hiding。假设除了stringEquals.getAsBoolean()之外,其余的代码对你来说是不可见的,所以我们不能再知道stringEquals的实现。
使用函数接口的好处是从客户端代码中decoupling提供者,例如:为了在示例代码中获得stringEquals的最终结果,客户端代码需要知道s1 & s2,但是函数接口端的提供者或消费者并不知道它。因为实现是在客户端封装的。这使得供应商端可以在不知道客户如何实现它的情况下完成其内部任务。有关更多详细信息,请参阅Separation of Concerns。
另一方面,定义一个函数接口实例并在相同的代码块中立即调用它是毫无意义的。
让我们看看Objects#requireNotNull的另一个具体示例,如下所示:
public static <T> T requireNonNull(T obj, Supplier<String> messageSupplier) {
if (obj == null)
throw new NullPointerException(messageSupplier.get());
// ^
// the `requireNotNull` method doesn't know how to create a diagnostic message
return obj;
}我们可以从间接组件(这里的函数接口)中获益。
stringEquals的lambda的主体,间接组件的调用的最终结果,等等...https://stackoverflow.com/questions/48352062
复制相似问题