浅析Java中的Optional
Optional是Java8引入的一个用于解决空指针异常的容器,它可以保存类型T的值,或者仅仅保存null。Optional提供很多有用的方法,这样我们就不用显式进行空值检测。
1. 引入
引言中对于Optional做了一个简单的介绍,它的主要是提供了一个解决空指针异常的容器。可能从字面上理解Optional有点懵,下面我们首先从它要解决的问题入手,看一下为什么要引入Optional。假设此时定义一个Person类,类的成员变量只有String的name,类的定义如下:
public class Person {
private String name;
public Person(String name) {
this.name = name;
}
public Person() {
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
}
然后我们通过new关键字来实例化Person类对象,并获取对象的name成员变量,最后将其转为小写形式。正常的代码可以写为:
Person p = new Person();
System.out.println(p.getName().toLowerCase());
但运行程序发现程序会抛出空指针异常。因为我们调用的空参构造方法,而且并没有通过setName()
设置对象的name属性,因此p.getName()
得到的String类型数据的默认值null,对null执行toLowerCase()
自然会抛异常。
Exception in thread "main" java.lang.NullPointerException
如果我们还是想执行上面的操作,同时又关注程序是否会抛出异常,代码可写成:
Person p = new Person();
String name = p.getName() == null ? p.getName() : null;
System.out.println(name); // null
此时如果对象的name属性有值,则会返回对应的值,否则返回null。如果程序中大量的地方都可能出现null,那么就需要每一处都进行一次判断,这样的方法会造成大量的冗余代码,而且并不高效。
而Optional的出现就很大程度上解决了这个问题,并且大力推进了Java中函数式编程的使用,下面我们就来具体看一下Optional这个泛型类。
2. 创建Optional实例
创建Optional实例一共有如下的四种方式:
-
Optional.empty()
:创建一个包装对象值为空的Optional对象 -
Optional.of(T value)
:创建包装对象值非空的Optional对象 -
Optional.ofNullable(T value)
:创建包装对象值允许为空的Optional对象 -
Optional<T> or(Supplier<? extends Optional<? extends T>> supplier)
:如果对象不为空,则返回该对象,否则返回由Supplier产生的另一个Optional对象
Optional<Object> o1 = Optional.empty();
System.out.println(o1); // Optional.empty
Optional<Object> o2 = Optional.of(10);
System.out.println(o2); // Optional[10]
Optional<Object> o4 = Optional.ofNullable(10);
System.out.println(o4); // Optional[10]
Optional<String> o = Optional.ofNullable("Forlogen").or(() -> Optional.ofNullable("Unknow"));
System.out.println(o.orElseGet(() -> "no value")); // Forlogen
Optional<Object> o1 = Optional.ofNullable(null).or(() -> Optional.ofNullable("unKnow"));
System.out.println(o1.orElseGet(() -> "no value")); // unKnow
of()
和ofNullable()
的区别在于:如果将null传入of()
时,程序会抛出空指针异常,而ofNullable()
会返回一个空的Optional对象,效果和empty()
一样。
Optional<Object> o3 = Optional.of(null);
System.out.println(o3); // Exception in thread "main" java.lang.NullPointerException
Optional<Object> o4 = Optional.ofNullable(null);
System.out.println(o4); // Optional.empty
而且当对使用empty()
实例化的对象和将通过of()
及ofNullable
创建的空Optional对象获取其中的值时,程序会抛出NoSuchElementException异常。
System.out.println(o1.get());
// Exception in thread "main" java.util.NoSuchElementException: No value present
System.out.println(o3.get());
// Exception in thread "main" java.util.NoSuchElementException: No value present
因此,永远不要使用null来创建Optional实例,以及应避免使用get()
来获取实例的值,至于值的获取下面来讲。
3. 获取Optional实例值
用于获取Optional对象值的方法一共有四种:
-
T get()
:获取Optional对象的值,但如果对象为空时,抛出NullPointerException异常,所以尽量不要使用它来获取值Optional<Integer> o1 = Optional.of(10); System.out.println(o1.get()); // 10
-
T ofElse(T value)
:如果包装对象值非空,返回包装对象值,否则返回传入的参数other的值(默认值),它是一种急性运算Optional<Integer> o2 = Optional.of(10); Integer num = o2.orElse(0); System.out.println(num); // 10 Person p = new Person("Forlogen"); Person person = Optional.of(p).orElse(null); System.out.println(person.getName()); // Forlogen Person p1 = new Person(); Person person1 = Optional.of(p1).orElse(null); System.out.println(person1.getName()); // null
-
T orElseGet (Supplier<? extends T> supplier)
:有值的时候返回值,如果没有值,返回执行传入的Supplier函数式接口后的结果,它是一种惰性计算Integer num1 = Optional.of(24).orElseGet(() -> 0); System.out.println(num1); // 24 Object o = Optional.ofNullable(null).orElseGet(() -> 0); System.out.println(o); // 0
-
<X extends Throwable> T orElseThrow?(Supplier<? extends X> exceptionSupplier)
:如果对象不为空,则返回对象的值,否则通过Supplier实例化一个Throwable异常,并在orElseThrow()中将异常抛出String s = Optional.of("Forlogen").orElseThrow(() -> new RuntimeException("no name")); System.out.println(s); // Forlogen Object s1 = Optional.ofNullable(null).orElseThrow(() -> new RuntimeException("no name")); System.out.println(s1); // Exception in thread "main" java.lang.RuntimeException: no name
4. 实例转换
类似于Stream中的关于Stream对象的转换方法一样,Optional同样提供了一些关于实例转换的方法,如下所示:
-
<U> Optional<U> map(Function<? super T,? extends U> mapper)
:方法将Optional中的包装对象用Function函数进行运算,并包装成新的Optional对象(包装对象的类型可能改变) -
<U> Optional<U> flatMap (Function<? super T,? extends Optional<? extends U>> mapper)
:方法传入的参数Function函数的返回值类型为Optional<U>类型,而不是U类型,它能将一个二维的Optional对象映射成一个一维的对象 -
Optional<T> filter (Predicate<? super T> predicate)
:方法接受参数为Predicate对象,用于对Optional对象进行过滤。如果符合Predicate的条件,返回Optional对象本身,否则返回一个空的Optional对象
Optional<String> s = Optional.of("Forlogen").map(w -> w.toUpperCase());
System.out.println(s.orElseGet(() -> "no value")); // FORLOGEN
Person p = new Person("Forlogen");
Optional<String> s1 = Optional.ofNullable(p).flatMap(u -> Optional.ofNullable(u.getName()));
System.out.println(s1); // Optional[Forlogen]
Optional<String> b = Optional.ofNullable("Forlogen").filter(w -> w.startsWith("F") && w.length() > 3);
System.out.println(b.isPresent()); // true
同样我们也可以通过链式编程来使用上面的方法:
Optional<String> str = Optional.ofNullable("Forlogen")
.filter(w -> w.length() > 4)
.map(String::toUpperCase);
System.out.println(str.orElseGet(() -> "no value")); // FORLOGEN
Optional<String> str1 = Optional.ofNullable("Kobe")
.filter(w -> w.length() > 4)
.map(String::toUpperCase);
System.out.println(str1.orElseGet(() -> "no value")); // no value
5. 判断方法
虽然前面创建Optional实例部分说到,尽量不要将null传入创建函数,但实际中无法避免创建得到空Optional对象的发生。因此,为了避免后面获取对象值时出现异常,应该在获取之前进行判断。Optional中提供了三个用于判断的方法:
-
boolean isPresent ()
:用于判断包装对象的值是否非空 -
void ifPresent (Consumer<? super T> action)
:方法接受一个Consumer对象,如果包装对象的值非空,则执行Consumer对象的accept()方法 -
void ifPresentOrElse(Consumer<? super T> action, Runnable emptyAction)
:如果对象包含值,会执行 Consumer 的动作,否则运行 Runnable
System.out.println(Optional.ofNullable(10).isPresent()); // true
System.out.println(Optional.ofNullable(null).isPresent()); // false
Optional.ofNullable("Forlogen").ifPresent(w -> System.out.println(w.toUpperCase())); // FORLOGEN
Optional.ofNullable(new Person("Forlogen"))
.ifPresentOrElse(u -> System.out.println(u.getName()),
()-> System.out.println("no name")); // Forlogen
Optional.ofNullable(new Person())
.ifPresentOrElse(u -> System.out.println(u.getName()),
()-> System.out.println("no name")); // null
6. 获取Stream流
-
Stream<T> stream()
:方法将Optional对象转换Stream对象,然后就可以使用Stream中的一系列方法
Optional.ofNullable("Forlogen").
stream().
filter(w -> w.length() > 4).
map(String::toUpperCase).
forEach(System.out::println); // FORLOGEN
7. 完整实验代码
import java.util.ArrayList;
import java.util.Comparator;
import java.util.Optional;
import java.util.stream.Stream;
public class OptionalDemo {
public static void main(String[] args) {
// CreateOptionalObject();
// getOptionalObject();
// ConvertOptionalObject();
// JudgeOptionalObject();
// NewOfOptional();
}
public static void NewOfOptional() {
Optional<String> o = Optional.ofNullable("Forlogen").or(() -> Optional.ofNullable("Unknow"));
System.out.println(o.orElseGet(() -> "no value")); // Forlogen
Optional<Object> o1 = Optional.ofNullable(null).or(() -> Optional.ofNullable("unKnow"));
System.out.println(o1.orElseGet(() -> "no value")); // unKnow
Optional.ofNullable("Forlogen").
stream().
filter(w -> w.length() > 4).
map(String::toUpperCase).
forEach(System.out::println); // FORLOGEN
}
public static void JudgeOptionalObject() {
System.out.println(Optional.ofNullable(10).isPresent()); // true
System.out.println(Optional.ofNullable(null).isPresent()); // false
Optional.ofNullable("Forlogen").ifPresent(w -> System.out.println(w.toUpperCase())); // FORLOGEN
System.out.println(Optional.ofNullable("Forlogen").toString()); // Optional[Forlogen]
Person p = new Person("Forlogen");
Optional.ofNullable(new Person("Forlogen")).ifPresentOrElse(u -> System.out.println(u.getName()), ()-> System.out.println("no name")); // Forlogen
Optional.ofNullable(new Person()).ifPresentOrElse(u -> System.out.println(u.getName()), ()-> System.out.println("no name")); // null
}
public static void ConvertOptionalObject() {
Optional<String> s = Optional.of("Forlogen").map(w -> w.toUpperCase());
System.out.println(s.orElseGet(() -> "no value")); // FORLOGEN
Person p = new Person("Forlogen");
Optional<String> s1 = Optional.ofNullable(p).flatMap(u -> Optional.ofNullable(u.getName()));
System.out.println(s1); // Optional[Forlogen]
Optional<String> b = Optional.ofNullable("Forlogen").filter(w -> w.startsWith("F") && w.length() > 3);
System.out.println(b.isPresent()); // true
Optional<String> str = Optional.ofNullable("Forlogen")
.filter(w -> w.length() > 4)
.map(String::toUpperCase);
System.out.println(str.orElseGet(() -> "no value")); // FORLOGEN
Optional<String> str1 = Optional.ofNullable("Kobe")
.filter(w -> w.length() > 4)
.map(String::toUpperCase);
System.out.println(str1.orElseGet(() -> "no value")); // no value
}
public static void getOptionalObject() {
Optional<Integer> o1 = Optional.of(10);
System.out.println(o1.get()); // 10
Optional<Integer> o2 = Optional.of(10);
Integer num = o2.orElse(0);
System.out.println(num); // 10
Person p = new Person("Forlogen");
Person person = Optional.of(p).orElse(null);
System.out.println(person.getName()); // Forlogen
Person p1 = new Person();
Person person1 = Optional.of(p1).orElse(null);
System.out.println(person1.getName()); // null
Integer num1 = Optional.of(24).orElseGet(() -> 0);
System.out.println(num1); // 24
Object o = Optional.ofNullable(null).orElseGet(() -> 0);
System.out.println(o); // 0
String s = Optional.of("Forlogen").orElseThrow(() -> new RuntimeException("no name"));
System.out.println(s); // Forlogen
Object s1 = Optional.ofNullable(null).orElseThrow(() -> new RuntimeException("no name"));
System.out.println(s1); // Exception in thread "main" java.lang.RuntimeException: no name
}
public static void CreateOptionalObject() {
Optional<Object> o1 = Optional.empty();
System.out.println(o1); // Optional.empty
Optional<Object> o2 = Optional.of(10);
System.out.println(o2); // Optional[10]
Optional<Object> o4 = Optional.ofNullable(null);
System.out.println(o4); // Optional.empty
}
}
8. 参考
上一篇: php怎么去掉字符串最后几个字符
下一篇: C#基于正则表达式去掉注释的方法示例