大纲
Optional的出现
我们看看在没有Optional这个类之前针对对象的判空操作:
/**
* 传统判空
*/
@Test
public void traditionEmpty01(){
// String username = "蔡瑁,张允";
String username = null;
// 按逗号切割
if(username != null){
String[] split = username.split(",");
System.out.println(Arrays.toString(split));
}else{
System.out.println("username is null");
}
}
/**
* 传统遍历集合 判空
*/
@Test
public void traditionEmpty02(){
List<String> names = Arrays.asList("曹仁", "文丑", "陆逊");
// 遍历集合前需要我们进行集合的判空,需要写一个if 比较麻烦
if(names != null || !names.isEmpty()){
for (String name : names) {
System.out.println(name);
}
}
}
很明显我们看到了if…else…,这样看起来就比较臃肿。
在平时写代码中,我们需要尽量少一些if等语句的嵌套。我们的大脑不像计算机能够记住这么多条件,每次嵌套就类似与我们需要在大脑里维护一个栈,代码阅读难度变高了。
之前安卓的同事和我说他用的Kotlin,判空比Java优雅。现在JDK8新出一个Optional类就是一个优雅判空的产生的。
Optional类的简单介绍
JDK8的Optional类是借鉴了谷歌的工具包guava的同名Optional,它们基本上相同,是为了优雅判空,防止出现NPE空指针异常。
使用Optional方法针对上面的第一个例子做优化,来看看是否是优雅一些。
/**
* 使用Optional优化第一个例子的传统判空
*/
@Test
public void optimizeTraditionEmpty() {
// String username = "蔡瑁,张允";
String username = null;
Optional.ofNullable(username).ifPresent(name -> {
String[] split = username.split(",");
System.out.println(Arrays.toString(split));
});
}
就算上面的username为空,也不会抛出空指针NPE异常。
Optional的创建
Optional类的构造方法都是私有的,我们无法通过new所以来创建Optional对象,只能通过它提供的API来创建该对象。通常有下面三种方式创建
-
empty()
:创建一个空的Optional实例 -
of(T value)
:创建一个封装了T对象的Optional实例 -
ofNullable(T value)
:创建一个封装了T对象的Optional实例
在Optional类中有一个重要的成员变量value,这个变量就是被封装的对象。
还有一个表示空的Optional的EMPTY。
private static final Optional<?> EMPTY = new Optional<>();
private final T value;
看下创建方法的源码:
// 很明显 直接拿EMPTY进行类型转换
public static<T> Optional<T> empty() {
Optional<T> t = (Optional<T>) EMPTY;
return t;
}
// 调用私有构造方法创建一个Optional对象,value为null时会抛出空指针异常
public static <T> Optional<T> of(T value) {
return new Optional<>(value);
}
// 传入的值为null时,创建空的Optional对象
// 不为空就创建封装指定值的Optional对象
// 这个最常用
public static <T> Optional<T> ofNullable(T value) {
return value == null ? empty() : of(value);
}
Optional的其他方法
get方法
显而易见,get方法就是直接获取Optional对象封装的对象,假如为null是会抛出找不到元素异常。
public T get() {
if (value == null) {
throw new NoSuchElementException("No value present");
}
return value;
}
isPresent方法
该方法是判断Optional封装的对象是否是空。
public boolean isPresent() {
return value != null;
}
为了演示该方法的用法,下面写一个例子,但是在实际编写代码的时候是不推荐这么写的。
@Test
public void isPresentDemo(){
String username = "lv meng"; // 吕蒙
Optional<String> op = Optional.ofNullable(username);
if(op.isPresent()){
String upperUsername = op.get().toUpperCase();
System.out.println(upperUsername);
}else{
System.out.println("username is null");
}
}
我们虽然使用了isPresent方法,但是它返回的还是布尔值,所以还是需要if判断,那Optional就失去意义了,所以一般不使用isPresent方法,有更好的方法来优化这个问题。
ifPresent方法
推荐使用ifPresent方法,假如Optional封装的值是null,则不做任务处理,很好的规避了NPE异常。需要我们传递一个消费者Consumer接口,做我们要做的处理就好了。
public void ifPresent(Consumer<? super T> consumer) {
if (value != null)
consumer.accept(value);
}
我们使用ifPresent方法来优化上面这个例子:
@Test
public void ifPresentDemo(){
String username = "lv meng"; // 吕蒙
Optional<String> op = Optional.ofNullable(username);
op.ifPresent(name ->{
System.out.println(name.toUpperCase());
});
}
filter方法
通过传入一个Predicate函数来对Optional封装的值进行过滤操作。假如满足过滤条件就返回当前Optional对象,不满足就返回一个空的Optional对象,也就是 empty()。
public Optional<T> filter(Predicate<? super T> predicate) {
Objects.requireNonNull(predicate);
if (!isPresent())
return this;
else
return predicate.test(value) ? this : empty();
}
判断某个数组长度是否大于3:
@Test
public void filterDemo(){
// 判断集合的长度是否大于3
List<String> nameList = Arrays.asList("关平", "徐庶", "诸葛村夫", "王司徒");
Optional optional = Optional.ofNullable(nameList).filter(names ->
names.size() > 3
);
System.out.println(optional);
}
map方法
map方法是一个转换方法,可以将一个类型转换成另一个类型,这是通过Function完成的。
public<U> Optional<U> map(Function<? super T, ? extends U> mapper) {
Objects.requireNonNull(mapper);
if (!isPresent())
return empty();
else {
return Optional.ofNullable(mapper.apply(value));
}
}
例子:将某个字符串转换成它的长度。
@Test
public void mapDemo(){
String username = "康帅博蜂蜜柚子";
Optional<Integer> op = Optional.ofNullable(username).map(name ->
name.length()
);
System.out.println(op);
}
flatMap方法
flatMap方法和map方法其实差不多,只不过Function函数里的apply方法的返回值类型是Optional类型的。
public<U> Optional<U> flatMap(Function<? super T, Optional<U>> mapper) {
Objects.requireNonNull(mapper);
if (!isPresent())
return empty();
else {
return Objects.requireNonNull(mapper.apply(value));
}
}
例子:将某个字符串转换成它的长度。
@Test
public void flatMapDemo(){
String username = "康帅博脑残牛肉面";
Optional<Integer> op = Optional.ofNullable(username).flatMap(name ->
Optional.ofNullable(name.length())
);
System.out.println(op);
}
orElse方法
orElse方法就是当Optional封装的值为null时,返回orElse传入的值作为替代。
public T orElse(T other) {
return value != null ? value : other;
}
例子:
@Test
public void orElseDemo(){
String username = null;
username = Optional.ofNullable(username).orElse("unkown name");
System.out.println(username);
}
orElseGet方法
该方法和orElse方法的区别是orElse方法是直接传入指定值的,而orElseGet需要传递一个生产接口。
public T orElseGet(Supplier<? extends T> other) {
return value != null ? value : other.get();
}
例子:
@Test
public void orElseGetDemo(){
String username = null;
username = Optional.ofNullable(username).orElseGet(()->{
String name = "東南西北中發";
return name.substring(4);
});
System.out.println(username); // 中發
}
orElseThrow方法
orElseThrow方法直接在生产接口Supplier创建一个异常对象就可以了,当Optional封装的对象的值是null时,直接抛出我们预先准备的异常。
public <X extends Throwable> T orElseThrow(Supplier<? extends X> exceptionSupplier) throws X {
if (value != null) {
return value;
} else {
throw exceptionSupplier.get();
}
}
例子:
@Test
public void orElseThrowDemo(){
String username = null;
username = Optional.ofNullable(username).orElseThrow(()->
new RuntimeException("username is null")
);
System.out.println(username);
}
小结
Optional是有一个可以为null的容器对象,正确使用Optional对象可以有效的避免空指针NPE异常,写出优雅的代码。
谨慎使用get方法,不要用isPresent的方法。Optional对象最重要的方法就是ifPresent方法了,多用几次就熟悉了。
原文始发于微信公众号(肝帝笔记):JDK8新特性Optional
版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 举报,一经查实,本站将立刻删除。
文章由极客之音整理,本文链接:https://www.bmabk.com/index.php/post/31535.html