若是你没有处理过空指针,那么你不是一位真正的 Java 程序员。java
NullPointException
。这是你可能会想,这报错很好处理,而后你看了眼报错行数,对比了下代码。脑海里瞬间闪过 ”对对对,这里有可能为空“,而后加上
null check
轻松处理。然而你不知道这已是你处理的第多少个空指针异常了。
为了解决上面的问题,在 Java SE8 中引入了一个新类 java.util.Optional
,这个类能够缓解上面的问题。git
你可能已经发现了,上面我用的是缓解而不是解决。这也是不少人理解不太对的地方,觉得 Java SE8 中的 Optional
类能够解决空指针问题。其实 Optional 类的的使用只是提示你这里可能存在空值,须要特殊处理,并提供了一些特殊处理的方法。若是你把 Optional
类看成空指针的救命稻草而不加思考的使用,那么依旧会碰到错误。程序员
由于 Optional
是的 Java SE8 中引入的,所以本文中不免会有一些 JDK8 中的语法,如 Lambda 表达式,流处理等,可是都是基本形式,不会有过于复杂的案例。github
Optional 的建立一共有三种方式。面试
/** * 建立一个 Optional */
@Test
public void createOptionalTest() {
// Optional 构造方式1 - of 传入的值不能为 null
Optional<String> helloOption = Optional.of("hello");
// Optional 构造方式2 - empty 一个空 optional
Optional<String> emptyOptional = Optional.empty();
// Optional 构造方式3 - ofNullable 支持传入 null 值的 optional
Optional<String> nullOptional = Optional.ofNullable(null);
}
复制代码
其中构造方式1中 of
方法,若是传入的值会空,会报出 NullPointerException
异常。函数
Optional 只是一个包装对象,想要判断里面有没有值可使用 isPresent
方法检查其中是否有值 。网站
/** * 检查是否有值 */
@Test
public void checkOptionalTest() {
Optional<String> helloOptional = Optional.of("Hello");
System.out.println(helloOptional.isPresent());
Optional<Object> emptyOptional = Optional.empty();
System.out.println(emptyOptional.isPresent());
}
复制代码
获得的输出:spa
true
false
复制代码
从 JDK11 开始,提供了 isEmpty
方法用来检查相反的结果:是否为空。.net
若是想要在有值的时候进行一下操做。可使用 ifPresent
方法。设计
/** * 若是有值,输出长度 */
@Test
public void whenIsPresent() {
// 若是没有值,获取默认值
Optional<String> helloOptional = Optional.of("Hello");
Optional<String> emptyOptional = Optional.empty();
helloOptional.ifPresent(s -> System.out.println(s.length()));
emptyOptional.ifPresent(s -> System.out.println(s.length()));
}
复制代码
输出结果:
5
复制代码
使用 get
方法能够获取值,可是若是值不存在,会抛出 NoSuchElementException
异常。
/** * 若是没有值,会抛异常 */
@Test
public void getTest() {
Optional<String> stringOptional = Optional.of("hello");
System.out.println(stringOptional.get());
// 若是没有值,会抛异常
Optional<String> emptyOptional = Optional.empty();
System.out.println(emptyOptional.get());
}
复制代码
获得结果:
hello
java.util.NoSuchElementException: No value present
at java.util.Optional.get(Optional.java:135)
at net.codingme.feature.jdk8.Jdk8Optional.getTest(Jdk8Optional.java:91)
复制代码
使用 orElse
, orElseGet
方法能够在没有值的状况下获取给定的默认值。
/** * 若是没有值,获取默认值 */
@Test
public void whenIsNullGetTest() {
// 若是没有值,获取默认值
Optional<String> emptyOptional = Optional.empty();
String orElse = emptyOptional.orElse("orElse default");
String orElseGet = emptyOptional.orElseGet(() -> "orElseGet default");
System.out.println(orElse);
System.out.println(orElseGet);
}
复制代码
获得的结果:
orElse default
orElseGet default
复制代码
看到这里你可能会有些疑惑了,这两个方法看起来效果是如出一辙的,为何会提供两个呢?下面再看一个例子,你会发现二者的区别。
/** * orElse 和 orElseGet 的区别 */
@Test
public void orElseAndOrElseGetTest() {
// 若是没有值,默认值
Optional<String> emptyOptional = Optional.empty();
System.out.println("空Optional.orElse");
String orElse = emptyOptional.orElse(getDefault());
System.out.println("空Optional.orElseGet");
String orElseGet = emptyOptional.orElseGet(() -> getDefault());
System.out.println("空Optional.orElse结果:"+orElse);
System.out.println("空Optional.orElseGet结果:"+orElseGet);
System.out.println("--------------------------------");
// 若是没有值,默认值
Optional<String> stringOptional = Optional.of("hello");
System.out.println("有值Optional.orElse");
orElse = stringOptional.orElse(getDefault());
System.out.println("有值Optional.orElseGet");
orElseGet = stringOptional.orElseGet(() -> getDefault());
System.out.println("有值Optional.orElse结果:"+orElse);
System.out.println("有值Optional.orElseGet结果:"+orElseGet);
}
public String getDefault() {
System.out.println(" 获取默认值中..run getDeafult method");
return "hello";
}
复制代码
获得的输出:
空Optional.orElse
获取默认值中..run getDeafult method
空Optional.orElseGet
获取默认值中..run getDeafult method
空Optional.orElse结果:hello
空Optional.orElseGet结果:hello
--------------------------------
有值Optional.orElse
获取默认值中..run getDeafult method
有值Optional.orElseGet
有值Optional.orElse结果:hello
有值Optional.orElseGet结果:hello
复制代码
在这个例子中会发现 orElseGet
传入的方法在有值的状况下并不会运行。而 orElse
却都会运行。
使用 orElseThrow
在没有值的时候抛出异常
/** * 若是没有值,抛出异常 */
@Test
public void whenIsNullThrowExceTest() throws Exception {
// 若是没有值,抛出异常
Optional<String> emptyOptional = Optional.empty();
String value = emptyOptional.orElseThrow(() -> new Exception("发现空值"));
System.out.println(value);
}
复制代码
获得结果:
java.lang.Exception: 发现空值
at net.codingme.feature.jdk8.Jdk8Optional.lambda$whenIsNullThrowExceTest$7(Jdk8Optional.java:118)
at java.util.Optional.orElseThrow(Optional.java:290)
at net.codingme.feature.jdk8.Jdk8Optional.whenIsNullThrowExceTest(Jdk8Optional.java:118)
复制代码
Optional
随 JDK8 一同出现,必然会有一些 JDK8 中的新特性,好比函数接口。Optional
中主要有三个传入函数接口的方法,分别是filter
,map
,flatMap
。这里面的实现实际上是 JDK8 的另外一个新特性了,所以这里只是简单演示,不作解释。后面放到其余 JDK8 新特性文章里介绍。
@Test
public void functionTest() {
// filter 过滤
Optional<Integer> optional123 = Optional.of(123);
optional123.filter(num -> num == 123).ifPresent(num -> System.out.println(num));
Optional<Integer> optional456 = Optional.of(456);
optional456.filter(num -> num == 123).ifPresent(num -> System.out.println(num));
// map 转换
Optional<Integer> optional789 = Optional.of(789);
optional789.map(String::valueOf).map(String::length).ifPresent(length -> System.out.println(length));
}
复制代码
获得结果:
123
3
复制代码
假设有计算机、声卡、usb 三种硬件(下面的代码中使用了 Lombok
的 @Data
注解)。
/** * 计算机 */
@Data
class Computer {
private Optional<SoundCard> soundCard;
}
/** * 声卡 */
@Data
class SoundCard {
private Optional<Usb> usb;
}
/** * USB */
@Data
class Usb {
private String version;
}
复制代码
计算机可能会有声卡,声卡可能会有 usb。那么怎么取得 usb 版本呢?
/** * 电脑里【有可能】有声卡 * 声卡【有可能】有USB接口 */
@Test
public void optionalTest() {
// 没有声卡,没有 Usb 的电脑
Computer computerNoUsb = new Computer();
computerNoUsb.setSoundCard(Optional.empty());
// 获取 usb 版本
Optional<Computer> computerOptional = Optional.ofNullable(computerNoUsb);
String version = computerOptional.flatMap(Computer::getSoundCard).flatMap(SoundCard::getUsb)
.map(Usb::getVersion).orElse("UNKNOWN");
System.out.println(version);
System.out.println("-----------------");
// 若是有值,则输出
SoundCard soundCard = new SoundCard();
Usb usb = new Usb();
usb.setVersion("2.0");
soundCard.setUsb(Optional.ofNullable(usb));
Optional<SoundCard> optionalSoundCard = Optional.ofNullable(soundCard);
optionalSoundCard.ifPresent(System.out::println);
// 若是有值,则输出
if (optionalSoundCard.isPresent()) {
System.out.println(optionalSoundCard.get());
}
// 输出没有值,则没有输出
Optional<SoundCard> optionalSoundCardEmpty = Optional.ofNullable(null);
optionalSoundCardEmpty.ifPresent(System.out::println);
System.out.println("-----------------");
// 筛选 Usb2.0
optionalSoundCard.map(SoundCard::getUsb)
.filter(usb1 -> "3.0".equals(usb1.map(Usb::getVersion)
.orElse("UBKNOW")))
.ifPresent(System.out::println);
}
复制代码
获得结果:
UNKNOWN
-----------------
SoundCard(usb=Optional[Usb(version=2.0)])
SoundCard(usb=Optional[Usb(version=2.0)])
-----------------
复制代码
在本文中,咱们看到了如何使用 Java SE8 的 java.util.Optional
类。Optional
类的目的不是为了替换代码中的每一个空引用,而是为了帮助更好的设计程序,让使用者能够仅经过观察属性类型就能够知道会不会有空值。另外,Optional
不提供直接获取值的方法,使用时会强迫你处理不存在的状况。间接的让你的程序免受空指针的影响。
文中代码已经上传 Github。
<完>
我的网站:www.codingme.net
若是你喜欢这篇文章,能够关注公众号,一块儿成长。
关注公众号回复资源能够没有套路的获取全网最火的的 Java 核心知识整理&面试资料。