【java】Optional操作

发布时间:2023年12月18日

参考:

1、简介

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表达式的使用能够让开发出的代码更简洁和优雅。

2、、Optional对象的创建

// 1、创建一个包装对象值为空的Optional对象
Optional<String> optStr = Optional.empty();

// 2、创建包装对象值非空的Optional对象
Optional<String> optStr1 = Optional.of("optional");

// 3、创建包装对象值允许为空的Optional对象
Optional<String> optStr2 = Optional.ofNullable(null);

(1)of创建

Student student = new Student("王五", 80);
Optional<Student> optional = Optional.of(student);

(2)ofNullable创建

Student student = new Student("王五", 80);
Optional<Student> optional = Optional.ofNullable(student);

(3)of 和 ofNullable区别

  • of:接收的值不能为 null,否则会报空指针异常
  • ofNullable:接收的值可以是 null,不会报空指针异常,但如果接收的值是是 null,在使用 get() 获取的时候就会报空指针
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();

3、判空处理

(1)isPresent()

功能:判断非空,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为空的操作");
}

(2)isEmpty()

功能:判断为空,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不为空的操作");
}

4、条件动作

(1)ifPresent()

功能:相当于判断 !=null , 不为空才执行里面的代码

Student student1 = new Student("王五", 80);
Optional<Student> optional = Optional.ofNullable(student1);

optional.ifPresent(student -> System.out.println("student1不为空,姓名为:"+student.getName()));

(2)ifPresentOrElse()

功能: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为空") );

5、get()获取值

功能:返回包装对象的实际值,但是如果包装对象值为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为空");
}

6、orElse()/orElseGet()/orElseThrow()

(1)orElse()

功能:orElse()方法功能比较简单,即如果包装对象值非空,返回包装对象值,否则返回入参other的值(默认值)

示例:

String gender = Optional.ofNullable(student)
    .map(u -> u.getGender())
    .orElse("Unkown");

(2)orElseGet()

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区别

  • 无论 Optional 中的值是否为空 orElse 中的代码都会执行,而 orElseGet 中的代码只有 Optional 中的值为空才会执行。
  • orElseGet 接收的是 Supplier 类型的参数,而 orElse 接受的是T类型的参数;
    当 student1 为空时 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);
}

(3)orElseThrow()

**功能:**orElseThrow()方法其实与orElseGet()方法非常相似了,入参都是Supplier对象,只不过orElseThrow()的Supplier对象必须返回一个Throwable异常,并在orElseThrow()中将异常抛出

String gender = Optional.ofNullable(student)
    .map(u -> u.getGender())
    .orElseThrow(() -> new RuntimeException("Unkown"));

7、or()

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

8、filter()过滤

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

9、map()转化值

功能: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();
}

10、flatMap()解包

参考:Optional中map和flatMap的区别

  • flatMap使用场景:Optional嵌套成二维时使用(即:Optional<Optional>),如果某对象实例的属性本身就为Optional包装过的类型,那么就要使用flatMap方法,就像School::getTearch返回的就是Optional类型的,所以不用再使用Optional进行包装;
  • map使用场景:对于返回值是其他类型,需要Optional进行包装,如Student::getName得到是String类型的,就需要使用map方法在其外面包装一层Optional对象。
    构建一个测试对象
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");
}

11、stream()

功能:在 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]
文章来源:https://blog.csdn.net/qq_31400717/article/details/134964919
本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。