java 补充日期
哇,人们真的对Java 9对Stream API的添加感兴趣。 想要更多? 让我们看一下……
可选的
可选::流
无需解释:
Stream<T> stream();
想到的第一个词是: 终于 ! 最后,我们可以轻松地从可选值流变为当前值流!
给定一个Optional findCustomer(String customerId)
我们必须执行以下操作:
public Stream<Customer> findCustomers(Collection<String> customerIds) {return customerIds.stream().map(this::findCustomer)// now we have a Stream<Optional<Customer>>.filter(Optional::isPresent).map(Optional::get);
}
或这个:
public Stream<Customer> findCustomers(Collection<String> customerIds) {return customerIds.stream().map(this::findCustomer).flatMap(customer -> customer.isPresent()? Stream.of(customer.get()): Stream.empty());
}
我们当然可以将其推入实用程序方法中(我希望您这样做了),但这仍然不是最佳方法。
现在,让Optional
实际上实现Stream
会很有趣,但是
- 在设计
Optional
时似乎没有考虑过它,并且 - 该船已经航行,因为溪流是懒惰的,而
Optional
不是。
因此,剩下的唯一选择是添加一个返回零个或一个元素流的方法。 有了这个,我们又有两个选择来实现期望的结果:
public Stream<Customer> findCustomers(Collection<String> customerIds) {return customerIds.stream().map(this::findCustomer).flatMap(Optional::stream)
}public Stream<Customer> findCustomers(Collection<String> customerIds) {return customerIds.stream().flatMap(id -> findCustomer(id).stream());
}
很难说我喜欢哪个更好-都有优点和缺点-但这是另一篇文章的讨论。 两者看起来都比我们之前要做的要好。
现在,我们可以对Optional进行延迟操作。
很难说我喜欢哪个更好-都有优点和缺点-但这是另一篇文章的讨论。 两者看起来都比我们之前要做的要好。
现在,我们可以对Optional进行延迟操作。
另一个小细节:如果愿意,我们现在可以更轻松地从Optional
上的急切操作转移到Stream
上的惰性操作。
public List<Order> findOrdersForCustomer(String customerId) {return findCustomer(customerId)// 'List<Order> getOrders(Customer)' is expensive;// this is 'Optional::map', which is eager.map(this::getOrders).orElse(new ArrayList<>());
}public Stream<Order> findOrdersForCustomer(String customerId) {return findCustomer(customerId).stream()// this is 'Stream::map', which is lazy.map(this::getOrders)
}
我想我还没有用例,但是记住这一点很好。
可选::或
最后让我思考的另外一个补充! 您有多长时间使用一次Optional
并想表达“使用此选项; 除非它是空的,否则在这种情况下我要使用另一个”? 很快我们就可以做到:
Optional<T> or(Supplier<Optional<T>> supplier);
假设我们需要一些客户数据,这些数据通常是从远程服务获得的。 但是因为访问它很昂贵并且非常聪明,所以我们有一个本地缓存。 实际上有两个,一个在内存上,一个在磁盘上。 (我可以看到你畏缩。放松,这只是一个例子。)
这是我们的本地API:
public interface Customers {Optional<Customer> findInMemory(String customerId);Optional<Customer> findOnDisk(String customerId);Optional<Customer> findRemotely(String customerId);}
在Java 8中将这些调用链接起来很麻烦(如果您不相信我,请尝试一下)。 但是使用Optional::or
成为小菜一碟:
public Optional<Customer> findCustomer(String customerId) {return customers.findInMemory(customerId).or(() -> customers.findOnDisk(customerId)).or(() -> customers.findRemotely(customerId));
}
那不是很酷吗? 没有它,我们怎么生活? 勉强可以告诉你。 只是勉强。
可选:: ifPresentOrElse
对于最后一个,我不太满意:
void ifPresentOrElse(Consumer<? super T> action, Runnable emptyAction);
您可以使用它来覆盖isPresent
-if的两个分支:
public void logLogin(String customerId) {findCustomer(customerId).ifPresentOrElse(this::logLogin,() -> logUnknownLogin(customerId));
}
logLogin
超载并且还带了一个客户,然后记录了其登录名。 同样, logUnknownLogin
记录未知客户的ID。
现在,我为什么不喜欢它? 因为它迫使我同时执行这两项操作,并且使我无法再进行进一步的链接。 我本来会更愿意这样做:
Optional<T> ifPresent(Consumer<? super T> action);Optional<T> ifEmpty(Runnable action);
上面的情况看起来类似,但更好:
public void logLogin(String customerId) {findCustomer(customerId).ifPresent(this::logLogin).ifEmpty(() -> logUnknownLogin(customerId));
}
首先,我发现它更具可读性。 其次,如果我愿意的话,它可以让我只拥有ifEmpty
分支(而不会因空lambda而使我的代码混乱)。 最后,它使我可以进一步链接这些电话。 要继续上面的示例:
public Optional<Customer> findCustomer(String customerId) {return customers.findInMemory(customerId).ifEmpty(() -> logCustomerNotInMemory(customerId)).or(() -> customers.findOnDisk(customerId)).ifEmpty(() -> logCustomerNotOnDisk(customerId)).or(() -> customers.findRemotely(customerId)).ifEmpty(() -> logCustomerNotOnRemote(customerId)).ifPresent(ignored -> logFoundCustomer(customerId));
}
剩下的问题如下:将返回类型添加到方法(在这种情况下为Optional::ifPresent
)是否是不兼容的更改? 不太明显,但我目前懒得去调查。 你知道吗?
反射
把它们加起来:
- 使用
Optional::stream
将Optional映射到Stream
。 - 使用
Optional::or
将空的Optional
替换为返回另一个Optional
的调用结果。 - 使用
Optional::ifPresentOrElse
可以执行isPresent-if
两个分支。
很酷!
你怎么看? 我敢肯定那里有人仍然会错过他最喜欢的手术。 告诉我怎么回事儿!
翻译自: https://www.javacodegeeks.com/2016/06/java-9-additions-optional.html
java 补充日期