Optional类是Java8为了解决null值判断问题,借鉴google guava类库的Optional类而引入的一个同名Optional类,使用Optional类可以避免显式的null值判断(null的防御性检查),避免null导致的NPE(NullPointerException)。
我们来看一段代码:
public static String getGender(Student student)
{
if(null == student)
{
return "Unkown";
}
return student.getGender();
}
这是一个获取学生性别的方法,方法入参为一个Student对象,为了防止student对象为null, 做了防御性检查:如果值为null,返回"Unkown"。
再看使用Optional优化后的方法:
public static String getGender(Student student)
{
return Optional.ofNullable(student).map(u -> u.getGender()).orElse("Unkown");
}
可以看到,Optional类结合lambda表达式的使用能够让开发出的代码更简洁和优雅。
// 1、创建一个包装对象值为空的Optional对象
Optional<String> optStr = Optional.empty();
// 2、创建包装对象值非空的Optional对象
Optional<String> optStr1 = Optional.of("optional");
// 3、创建包装对象值允许为空的Optional对象
Optional<String> optStr2 = Optional.ofNullable(null);
Student student = new Student("王五", 80);
Optional<Student> optional = Optional.of(student);
Student student = new Student("王五", 80);
Optional<Student> optional = Optional.ofNullable(student);
Student student = null;
//of 里只要传的参数只要是 null,就会报空指针异常
Optional<Student> optional = Optional.of(student);
Student student = null;
//ofNullable 接收的值可以为 null
Optional<Student> optional = Optional.ofNullable(student);
//如果上面ofNullable 里接收的值 为 null ,下面使用 get() 获取对象会报空指针异常
Student student1 = optional.get();
功能:判断非空,isPresent 相当于 !=null
Student student1 = new Student("王五", 80);
Optional<Student> optional = Optional.ofNullable(student1);
//将输出:student1不为空的操作
if (optional.isPresent()){
System.out.println("student1不为空的操作");
}else {
System.out.println("student1为空的操作");
}
Student student2 = null;
Optional<Student> optional2 = Optional.ofNullable(student2);
//将输出:student2为空的操作
if (optional2.isPresent()){
System.out.println("student2不为空的操作");
}else {
System.out.println("student2为空的操作");
}
功能:判断为空,isEmpty 相当于 ==null,Java 11 开始,我们可以使用 isEmpty 方法来处理相反的操作
Student student1 = new Student("王五", 80);
Optional<Student> optional = Optional.ofNullable(student1);
//将输出:student1不为空的操作
if (optional.isEmpty()){
System.out.println("student1为空的操作");
}else {
System.out.println("student1不为空的操作");
}
Student student2 = null;
Optional<Student> optional2 = Optional.ofNullable(student2);
//将输出:student2为空的操作
if (optional2.isEmpty()){
System.out.println("student2为空的操作");
}else {
System.out.println("student2不为空的操作");
}
功能:相当于判断 !=null , 不为空才执行里面的代码
Student student1 = new Student("王五", 80);
Optional<Student> optional = Optional.ofNullable(student1);
optional.ifPresent(student -> System.out.println("student1不为空,姓名为:"+student.getName()));
功能:java 9里新增了 ifPresentOrElse(),当 Optional 里的值不为空则执行第一个参数里的代码,为空则执行第二个参数的代码,相当于 if-else
Student student = new Student("王五", 80);
Optional<Student> optional = Optional.ofNullable(student);
optional.ifPresentOrElse(value -> System.out.println("student不为空,姓名:"+value.getName()), () -> System.out.println("student为空") );
功能:返回包装对象的实际值,但是如果包装对象值为null,会抛出NoSuchElementException异常
示例:
需要注意的是如果 student1 为 null,在使用 get() 获取值的时候会抛出 NoSuchElementException 异常:
(下面的写法只是样例,并不可取,后面会有几种代替 isPresent()和 get() )
Student student1 = new Student("王五", 80);
Optional<Student> optional = Optional.ofNullable(student1);
Student myStudent = optional.get();
System.out.println("姓名:"+myStudent.getName());//输出姓名:王五
使用以下写法替代上面
Student student1 = null;
Optional<Student> optional = Optional.ofNullable(student1);
if (optional.isPresent()){
Student myStudent = optional.get();
System.out.println("姓名:"+myStudent.getName());
}else {
System.out.println("student1为空");
}
功能:orElse()方法功能比较简单,即如果包装对象值非空,返回包装对象值,否则返回入参other的值(默认值)
示例:
String gender = Optional.ofNullable(student)
.map(u -> u.getGender())
.orElse("Unkown");
orElse 与 orElseGet 可替代 get 操作并且可以避免 get 操作出现的NoSuchElementException 异常
使用 orElse:
Student student1 = new Student("王五", 80);
Student student2 = new Student("张三", 90);
Optional<Student> optional = Optional.ofNullable(student1);
//先执行orElse里的语句,然后判断student1不为空则返回student1给 student3,否则返回student2给student3
Student student3 = optional.orElse(student2);
使用 orElseGet:
Student student1 = new Student("王五", 80);
Student student2 = new Student("张三", 90);
Optional<Student> optional = Optional.ofNullable(student1);
//判断student1不为空则返回student1给 student3,否则返回student2给student3
Student student3 = optional.orElseGet(()->student2);
orElse与orElseGet区别:
public static void main(String[] args) throws Exception {
Student student1 = null;
Optional<Student> optional = Optional.ofNullable(student1);
Student student2 = optional.orElse(getMyStudent());
Student student3 = optional.orElseGet(() -> getMyStudent());
}
public static Student getMyStudent() {
System.out.println("开始执行getMyStudent");
return new Student("张三", 90);
}
当 student1 不为空时只有 orElse 会执行:
public static void main(String[] args) throws Exception {
Student student1 = new Student("王五", 80);
Optional<Student> optional = Optional.ofNullable(student1);
Student student2 = optional.orElse(getMyStudent());
Student student3 = optional.orElseGet(() -> getMyStudent());
}
public static Student getMyStudent() {
System.out.println("开始执行getMyStudent");
return new Student("张三", 90);
}
**功能:**orElseThrow()方法其实与orElseGet()方法非常相似了,入参都是Supplier对象,只不过orElseThrow()的Supplier对象必须返回一个Throwable异常,并在orElseThrow()中将异常抛出
String gender = Optional.ofNullable(student)
.map(u -> u.getGender())
.orElseThrow(() -> new RuntimeException("Unkown"));
Java 9引入了 or() 方法,or 操作与 orElse 和 orElseGet 操作相似,只不过 or 操作返回的是一个新的Optional 对象,而 orElse 和 orElseGet 操作返回的是 Optional 中的值
String expected = null;
Optional<String> value = Optional.ofNullable(expected);
Optional<String> defaultValue = Optional.of("default");
//or()方法将返回一个新的Optional对象
Optional<String> result = value.or(() -> defaultValue);
System.out.println(result.get());//default
filter 将谓词作为参数并返回一个 Optional 对象
Integer year = 2022;
Optional<Integer> yearOptional = Optional.ofNullable(year);
boolean is2022 = yearOptional.filter(y -> y == 2016).isPresent();
System.out.println(is2022);//true
boolean is2023 = yearOptional.filter(y -> y == 2017).isPresent();
System.out.println(is2023);//false
功能:map()方法将Optional中的包装对象用Function函数进行运算,并包装成新的Optional对象(包装对象的类型可能改变)
示例:
先用ofNullable()方法构造一个Optional对象,然后用map()计算学生的年龄,返回Optional对象(如果student为null, 返回map()方法返回一个空的Optinal对象)
map()
public static Optional<Integer> getAge(Student student)
{
return Optional.ofNullable(student).map(u -> u.getAge());
}
map()+orElse()
Student student1 = new Student("aaa", 80);
Optional<Student> optional = Optional.ofNullable(student1);
String stuName=optional.map(u -> u.getName())
.map(name -> name.toUpperCase())
.orElse(null);
System.out.println(stuName);
filter()+map()
public boolean priceIsInRange1(Modem modem) {
return Optional.ofNullable(modem2)
.map(Modem::getPrice)
.filter(p -> p >= 10)
.filter(p -> p <= 15)
.isPresent();
}
public class School {
private String name;
private Optional<Tearch> tearch; //属性为Option封装的Tearch对象
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public Optional<Tearch> getTearch() {
return tearch;
}
public void setTearch(Optional<Tearch> tearch) {
this.tearch = tearch;
}
}
class Tearch{
private String name;
private Optional<Student> student;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public Optional<Student> getStudent() {
return student;
}
public void setStudent(Optional<Student> student) {
this.student = student;
}
}
class Student{
private String name;
private int age;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
}
如果给你一个School对象,让你得到Student的name属性的值,可以使用下面的方式:
错的方式:
School::getTearch会返回School实例的tearch属性,而tearch属性是使用Optional包装的Tearch对象,所以使用了map(School::getTearch),会返回Optional<Optional>对象,而不是我们所想的Optional
public static String getStudentName(School school){
return Optional.ofNullable(school)
.map(School::getTearch)
.map(Tearch::getStudent)
.map(Student::getName)
.orElse("false");
}
正确的做饭:flatMap
public static String getStudentName(School school){
return Optional.ofNullable(school)
.flatMap(School::getTearch)
.flatMap(Tearch::getStudent)
.map(Student::getName).orElse("false");
}
功能:在 Java 9中添加到 Optional 类的最后一个方法是 stream() 方法,允许我们将 Optional 实例视为Stream
Optional<List<String>> value = Optional.of(Arrays.asList("aaaa","bbbb","ccc"));
List<String> collect = value.stream()
.flatMap(Collection::stream)
.filter(v->v.length()==4)
.map(String::toUpperCase)
.collect(Collectors.toList());
System.out.println(collect);//[AAAA, BBBB]