当前位置:网站首页>optional类,便利函数,创建Optional,Optional对象操作以及Optional流
optional类,便利函数,创建Optional,Optional对象操作以及Optional流
2022-06-21 09:28:00 【凄戚】
java.util.optional
前言
我们必须考虑在一个空流中获取元素会发生什么,我们总是习惯流是不会中断的。然而,在流中放置null却会轻易令其中断,那么是否可以有一种对象,可以使我们即在持有流元素的同时,也可以在元素不存在时也向我们进行友好提示呢(不会产生异常)?
一、 Optional 类
Optional 可以实现这样的功能。标准流操作中的一些可以返回 Optional 对象,因为它们并不能保证预期结果一定存在。
findFirst():返回包含第一个元素的 Optional 对象,如果流为空则返回Optional.empty。findAny():返回包含任意元素的 Optional 对象,如果流为空则返回Optional.empty。max()和min():返回一个包含最大值或是最小值的 Optional 对象,如果流为空则返回Optional.empty。
reduce()不再以identity形式开头,而是将其返回值包含在 Optional 中。(identity 对象成为其他形式的reduce()的默认结果,因此不存在空结果的风险)对于数字流 IntStream 等,average()会将结果包装在 Optional 以防止流为空。
public class OptionalsFromEmptyStreams {
public static void main(String[] args) {
System.out.println(Stream.<String>empty()
.findFirst());
System.out.println(Stream.<String>empty()
.findAny());
System.out.println(Stream.<String>empty()
.max(String.CASE_INSENSITIVE_ORDER));
System.out.println(Stream.<String>empty()
.min(String.CASE_INSENSITIVE_ORDER));
System.out.println(Stream.<String>empty()
.reduce((s1, s2) -> s1 + s2));
System.out.println(IntStream.empty()
.average());
}
}
/* output: Optional.empty Optional.empty Optional.empty Optional.empty Optional.empty OptionalDouble.empty */
当流为空的时候你会获得一个Optional.empty 对象,而不是抛出异常。Optional 拥有toString()方法可以用于展示有用信息。
注意:空流是通过Stream.<String>empty()创建的。如果在没有任何上下文环境的情况下调用Stream.empty(),Java 并不知道它的数据类型。
下面是Optional 的基本用法:
public class OptionalBasics {
static void test(Optional<String> optString) {
if (optString.isPresent())
System.out.println(optString.get());
else
System.out.println("Nothing inside!");
}
public static void main(String[] args) {
test(Stream.of("Epithets").findFirst());
test(Stream.<String>empty().findFirst());
}
}
/* output: Epithets Nothing inside! */
当你接收到Optional 对象时,应首先调用isPresent()检查其中是否包含元素,如果存在,可使用get()获取。
二、 便利函数
有许多便利函数可以解包 Optional ,
isPresent(Consumer):当值存在时调用 Consumer,否则什么也不做。orElse(otherObject):如果值存在则直接返回,否则生成 otherObject。orElseGet(Supplier):如果只存在则直接返回,否则使用Supplier 函数生成一个可替代对象。orElseThrow(Supplier):如果值存在直接返回,否则使用Supplier 函数生成一个异常。
public class Optionals {
static void basics(Optional<String> optString) {
if (optString.isPresent()) {
System.out.println(optString.get());
} else System.out.println("Nothing Inside!");
}
static void ifPresent(Optional<String> optString) {
optString.ifPresent(System.out::println);
}
static void orElse(Optional<String> optString) {
System.out.println(optString.orElse("Nada"));
}
static void orElseGet(Optional<String> optString) {
System.out.println(optString.orElseGet(() -> "Generated"));
}
static void orElseThrow(Optional<String> optString) {
try {
System.out.println(optString.orElseThrow(() -> new Exception("Supplied")));
} catch (Exception e) {
System.out.println("Caught " + e);
}
}
static void test(String testName, Consumer<Optional<String>> cos) {
System.out.println("===" + testName + "=====");
cos.accept(Stream.of("Epithets").findFirst());
cos.accept(Stream.<String>empty().findFirst());
}
public static void main(String[] args) {
test("basics", Optionals::basics);
test("ifPresent", Optionals::ifPresent);
test("orElse", Optionals::orElse);
test("orElseGet", Optionals::orElseGet);
test("orElseThrow", Optionals::orElseThrow);
}
}
/* output: ===basics===== Epithets Nothing Inside! ===ifPresent===== Epithets ===orElse===== Epithets Nada ===orElseGet===== Epithets Generated ===orElseThrow===== Epithets Caught java.lang.Exception: Supplied */
三、 创建 Optional
当我们在自己的代码块中加入 Optional 时,可以使用以下三个静态方法:
empty():生成一个空 Optional。of(value):将一个非空值包装到 Optional 中。ofNullable():针对一个可能为空的值,为空时自动生成 Optional.empty,否则将值包装在 Optional 中。
下面是示例:
public class CreatingOptionals {
static void test(String testName, Optional<String> optional) {
System.out.println("====" + testName + "====");
System.out.println(optional.orElse("NULL"));
}
public static void main(String[] args) {
test("empty", Optional.empty());
test("of", Optional.of("Howdy()"));
try {
test("of", Optional.of(null));
} catch (Exception e) {
System.out.println(e);
}
test("ofNullable", Optional.ofNullable("Hi"));
test("ofNullable", Optional.ofNullable(null));
}
}
/* output: ====empty==== NULL ====of==== Howdy() java.lang.NullPointerException ====ofNullable==== Hi ====ofNullable==== NULL */
我们不能通过传递 null 到 of() 来创建 Optional 对象,最安全的方法是:使用 ofNullable() 来优雅的处理 null。
四、 Optional 对象操作:filter、map、flatMap
当我们的流管道生产了 Optional 对象,下面三个方法可使得 Optional 的后续能做更多的操作:
filter(Predicate):对 Optional 中的内容应用 Predicate 并将结果返回。如果 Optional 不满足 Predicate ,将 Optional 转化为空 Optional。如果 Optional 为空,则直接返回空 Optional。map(Function):如果 Optional 不为空,应用 Function 于 Optional 中的内容,并返回结果。否则直接返回 Optional.empty。flatMap(Function):同map(),但是提供的映射函数(就是Function)将结果包装在 Optional 对象中,因此 flatMap() 不会在最后进行任何包装。
public class OptionalFilter {
static String[] elements = {
"Foo", "Bar", "Baz", "Bingo"};
static Stream<String> testStream() {
return Arrays.stream(elements);
}
static void test(String descr, Predicate<String> predicate) {
System.out.println("---(" + descr + ")----");
for (int i = 0; i <= elements.length; i++) {
System.out.println(testStream()
.skip(i)
.findFirst()
.filter(predicate));
}
}
public static void main(String[] args) {
test("true", s -> true);
test("false", s -> false);
test("s != \"\"", s -> s != "");
//此处的s.length() 是传入的单个流元素
test("s.length() == 3", s -> s.length() == 3);
test("startsWith(\"B\")", s -> s.startsWith("B"));
}
}
/* output: ---(true)---- Optional[Foo] Optional[Bar] Optional[Baz] Optional[Bingo] Optional.empty ---(false)---- Optional.empty Optional.empty Optional.empty Optional.empty Optional.empty ---(s != "")---- Optional[Foo] Optional[Bar] Optional[Baz] Optional[Bingo] Optional.empty ---(s.length() == 3)---- Optional[Foo] Optional[Bar] Optional[Baz] Optional.empty Optional.empty ---(startsWith("B"))---- Optional.empty Optional[Bar] Optional[Baz] Optional[Bingo] Optional.empty */
即使输出看起来像流,要特别注意 test() 中的for循环:1.每次for循环都操作的是新生成的流,2.跳过指定数量的元素,3.通过调用findFirst()获取剩余元素中的第一个元素,并将其包装在 Optional 对象中。
注意:这里不同于普通for 循环,这里的 等于 号实际上使得最后一位超出了数组转化为的流的范围,但是并不会报错。
同map()一样,Optional.map()仅仅在 Optional 不为空时才执行这个映射函数,并将 Optional 的内容提取出来,传递给映射函数。
public class OptionalMap {
static String[] elements = {
"12", "", "23", "45"};
static Stream<String> stream() {
return Arrays.stream(elements);
}
static void test(String descr, Function<String,String> func) {
System.out.println("---(" + descr + ")----");
for (int i = 0; i <= elements.length; i++) {
System.out.println(stream()
.skip(i)
.findFirst()//此处产生一个optional
.map(func));
}
}
public static void main(String[] args) {
test("Add brackets", s -> "[" + s + "]");
test("Increment", s -> {
try {
return Integer.parseInt(s) + 1 + "";
} catch (Exception e) {
return s;
}
});
test("Replace", s -> s.replace("2", "9"));
//记得每个s是单个流元素
test("Take last digit", s -> s.length() > 0 ? s.charAt(s.length() - 1) + "" : s);
}
}
/* output: ---(Add brackets)---- Optional[[12]] Optional[[]] Optional[[23]] Optional[[45]] Optional.empty ---(Increment)---- Optional[13] Optional[] Optional[24] Optional[46] Optional.empty ---(Replace)---- Optional[19] Optional[] Optional[93] Optional[45] Optional.empty ---(Take last digit)---- Optional[2] Optional[] Optional[3] Optional[5] Optional.empty */
映射函数的返回结果会自动包装成为 Optional。Optional.empty() (空)会被直接跳过。
public class OptionalFlatMap {
static String[] elements = {
"12","", "23", "45"};
static Stream<String> stream() {
return Arrays.stream(elements);
}
static void test(String descr, Function<String, Optional<String>> func) {
System.out.println("---(" + descr + ")----");
for (int i = 0; i <= elements.length; i++) {
System.out.println(stream()
.skip(i)
.findFirst()
.flatMap(func));//这里的参数变成了Optional类型
}
}
public static void main(String[] args) {
test("Add brackets", new Function<String, Optional<String>>() {
@Override
public Optional<String> apply(String s) {
return Optional.of("[" + s + "]");
}
});
test("Increment",s -> {
try {
return Optional.of(Integer.parseInt(s) + 1 + "");
} catch (Exception e) {
return Optional.of(s);
}
});
test("Replace", s -> Optional.ofNullable(s.replace("2", "9")));
test("Take last digit",s->Optional.of(s.length()>0?s.charAt(s.length()-1)+"":s));
}
}
/* output: ---(Add brackets)---- Optional[[12]] Optional[[]] Optional[[23]] Optional[[45]] Optional.empty ---(Increment)---- Optional[13] Optional[] Optional[24] Optional[46] Optional.empty ---(Replace)---- Optional[19] Optional[] Optional[93] Optional[45] Optional.empty ---(Take last digit)---- Optional[2] Optional[] Optional[3] Optional[5] Optional.empty */
Optional 的 flatMap()应用于已生成 Optional 的映射函数,所以它不会像 map()那样将结果封装在 Optional 里。
同map(),flatMap()将提取非空的Optional 内容将其应用在映射函数。唯一的区别就是它不再将结果包装在 Optional 中,因为已经包装过了。
五、 Optional 流
假设你的生成器可能产生 null 值。那么当用它来创建流时,用 Optional 来包装元素更好。
public class Signal {
private final String msg;
public Signal(String msg) {
this.msg = msg;
}
public String getMsg() {
return msg;
}
@Override
public String toString() {
return "Signal{" +
"msg='" + msg + '\'' +
'}';
}
static Random rand = new Random(47);
public static Signal morse() {
switch (rand.nextInt(4)) {
case 1:
return new Signal("dot");
case 2:
return new Signal("dash");
default:
return null;
}
}
public static Stream<Optional<Signal>> stream() {
return Stream.generate(Signal::morse)
.map(signal -> Optional.ofNullable(signal));
}
}
当我们使用这个流的时候,必须要弄清楚如何解包 Optional 。代码如下:
public class StreamOfOptionals {
public static void main(String[] args) {
Signal.stream()
.limit(10)
.forEach(System.out::println);
System.out.println("-----------");
Signal.stream()
.limit(10)
.filter(Optional::isPresent)
.map(Optional::get)
.forEach(System.out::println);
}
}
/* output: Optional[Signal{msg='dash'}] Optional[Signal{msg='dot'}] Optional[Signal{msg='dash'}] Optional.empty Optional.empty Optional[Signal{msg='dash'}] Optional.empty Optional[Signal{msg='dot'}] Optional[Signal{msg='dash'}] Optional[Signal{msg='dash'}] ----------- Signal{msg='dot'} Signal{msg='dot'} Signal{msg='dash'} Signal{msg='dash'} */
在这里我们使用filter()来保留那些非空 Optional, 然后在 map() 中使用get()获取元素。由于每种情况都需要定义“空值”的含义,所以通常我们要为每个应用程序采用不同的方法。
边栏推荐
- 应用配置管理,基础原理分析
- stm32mp1 Cortex M4开发篇11:扩展板蜂鸣器控制
- Vuforia引擎支持的版本
- [actual combat] STM32 FreeRTOS migration series tutorial 7: FreeRTOS event flag group
- Ali has been working for 8 years. This learning note is left when he reaches P8. He has helped his friends get 10 offers
- Topic34——31. Next spread
- Full stack development
- TC software outline design document (mobile group control)
- finally block can not complete normally
- The R language uses the fix function to modify the name of the data variable through the editor. For example, use the fix function to modify the name of the dataframe data column
猜你喜欢
![[actual combat] STM32 FreeRTOS migration series tutorial 2: FreeRTOS mutually exclusive semaphores](/img/df/197fc0861e004238a84766681c6e26.jpg)
[actual combat] STM32 FreeRTOS migration series tutorial 2: FreeRTOS mutually exclusive semaphores

The internal structure of MySQL and how an SQL statement is executed

Solve the problem of error when typescript object gets value

It is said that this year gold three silver four has become gold one silver two.

TC软件概要设计文档(手机群控)

智能制造的下一站:云原生+边缘计算双轮驱动

Lei niukesi --- basis of embedded AI

一条命令开启监控之旅!

Stm32mp1 cortex M4 development part 13: external interrupt of expansion board key

【实战】STM32 FreeRTOS移植系列教程7:FreeRTOS 事件标志组
随机推荐
嵌入式软件项目流程、项目启动说明书(示例)
The @transactional in JUnit disappears. Can @rollback test the flag of rollback?
Stm32mp1 cortex M4 Development Chapter 11: expansion board buzzer control
The skill of using ADB and the principle of USB communication
智能制造的下一站:云原生+边缘计算双轮驱动
113. summary of common usage of moment.js
Unity vuforia recommended equipment
Retrofit扩展阅读
Pingcap was selected as the "voice of customers" of Gartner cloud database in 2022, and won the highest score of "outstanding performer"
Ali has been working for 8 years. This learning note is left when he reaches P8. He has helped his friends get 10 offers
Telecommuting Market Research Report
Alibaba P6 employees came to a small company for an interview and asked for an annual salary increase of 500000 yuan. How dare you speak
R language uses the < - operator to create a new variable, uses the existing data column (sum, mean) to create a new data column, uses the ifelse function or conditional judgment to create a discrete
[vs], [usage problem], [solution] when VS2010 is opened, it stays in the startup interface
Full stack development
Style penetration of vant UI components -- that is, some styles in vant UI components cannot be modified
Judge the data type of JS
Job hopping is better than promotion
An app developed based on retrotfit2.1+material design+ijkplayer
在使用各种软件时 积累的快捷键