当前位置: 代码迷 >> 综合 >> Java 基础 (18) -- Java 8 新特性:Optional
  详细解决方案

Java 基础 (18) -- Java 8 新特性:Optional

热度:92   发布时间:2023-12-16 13:23:44.0

文章目录

    • 1. 简介
    • 2. 常用方法
    • 3. 基本使用
      • 1. 创建 Optional 对象
      • 2. 判断值是否存在
      • 3. 非空表达式
      • 4. 设置(获取)默认值
      • 5. 获取值
      • 6. 过滤值
      • 7. 转换值
    • 4. 案例

1. 简介

通常在日常的开发中,总是无法难以避免会出现空指针异常的错误。OPtional 类是一个容器类,代表一个值存在或者不存在,可以用来避免空指针异常。

2. 常用方法

  1. Optional.of(T t):静态方法,创建一个基于T的Optional实例,不能用该方法创建空实例,否则会报空指针异常
  2. Optional.empty():静态方法,创建一个空的Optional实例
  3. Optional.ofNullable(T t):静态方法,若t不为null,则创建基于 T 的 Optional实例,若 t 为null,则创建空的 Optional 实例
  4. optionl.isPresent():判断optional对象是否有值,有则返回 true,否则 false
  5. isEmpty():与 isPresent() 相反的结果
  6. optional.orElse(T t):若optional不为空,则返回optional实例子,否则返回T的Optional实例
  7. orElseGet(Supplier s):若对象包含值,则返回值,否则返回s获取的值
  8. map(Function f):若有值,返回操作后的Optional实例,否则返回空的Optional实例
  9. flatMap(Function mapper):与map类似,返回值是Optional

3. 基本使用

1. 创建 Optional 对象

  1. 使用静态方法 empty() 创建一个空的 Optional 对象
    Optional<String> empty = Optional.empty();
    System.out.println(empty); // 输出:Optional.empty
    
  2. 使用静态方法 of() 创建一个非空的 Optional 对象
    Optional<String> opt = Optional.of("沉默王二");
    System.out.println(opt); // 输出:Optional[沉默王二]
    
    传递给 of() 方法的参数必须是非空的,也就是说不能为 null,否则仍然会抛出 NullPointerException
  3. 使用静态方法 ofNullable() 创建一个即可空又可非空的 Optional 对象
    String name = null;
    Optional<String> optOrNull = Optional.ofNullable(name);
    System.out.println(optOrNull); // 输出:Optional.empty
    
    ofNullable() 方法内部有一个三元表达式,如果为参数为 null,则返回私有常量 EMPTY;否则使用 new 关键字创建了一个新的 Optional 对象——不会再抛出 NPE 异常了

2. 判断值是否存在

  1. 可以通过方法 isPresent() 判断一个 Optional 对象是否存在,如果存在,该方法返回 true,否则返回 false。取代了 obj != null 的判断
    Optional<String> opt = Optional.of("沉默王二");
    System.out.println(opt.isPresent()); // 输出:trueOptional<String> optOrNull = Optional.ofNullable(null);
    System.out.println(opt.isPresent()); // 输出:false
    
  2. Java 11 后还可以通过方法 isEmpty() 判断与 isPresent() 相反的结果
    Optional<String> opt = Optional.of("沉默王二");
    System.out.println(opt.isEmpty()); // 输出:falseOptional<String> optOrNull = Optional.ofNullable(null);
    System.out.println(opt.isEmpty()); // 输出:true
    

3. 非空表达式

  1. Optional 类有一个非常现代化的方法:ifPresent(),允许我们使用函数式编程的方式执行一些代码,因此,我把它称为非空表达式。如果没有该方法的话,我们通常需要先通过 isPresent() 方法对 Optional 对象进行判空后再执行相应的代码:
    Optional<String> optOrNull = Optional.ofNullable(null);
    if (optOrNull.isPresent()) {
          System.out.println(optOrNull.get().length());
    }
    
  2. 有了 ifPresent() 之后,情况就完全不同了,可以直接将 Lambda 表达式传递给该方法,代码更加简洁,更加直观
    Optional<String> opt = Optional.of("沉默王二");
    opt.ifPresent(str -> System.out.println(str.length()));
    
  3. Java 9 后还可以通过方法 ifPresentOrElse(action, emptyAction) 执行两种结果,非空时执行 action,空时执行 emptyAction
    Optional<String> opt = Optional.of("沉默王二");
    opt.ifPresentOrElse(str -> System.out.println(str.length()), () -> System.out.println("为空"));
    

4. 设置(获取)默认值

有时候,我们在创建(获取) Optional 对象的时候,需要一个默认值,orElse() 和 orElseGet() 方法就派上用场了

  1. orElse() 方法用于返回包裹在 Optional 对象中的值,如果该值不为 null,则返回;否则返回默认值。该方法的参数类型和值得类型一致
    String nullName = null;
    String name = Optional.ofNullable(nullName).orElse("沉默王二");
    System.out.println(name); // 输出:沉默王二
    
  2. orElseGet() 方法与 orElse() 方法类似,但参数类型不同。如果 Optional 对象中的值为 null,则执行参数中的函数
    String nullName = null;
    String name = Optional.ofNullable(nullName).orElseGet(()->"沉默王二");
    System.out.println(name); // 输出:沉默王二
    
    从输出结果以及代码的形式上来看,这两个方法极其相似,这不免引起我们的怀疑,Java 类库的设计者有必要这样做吗?假设现在有这样一个获取默认值的方法,很传统的方式。
    public static String getDefaultValue() {
          System.out.println("getDefaultValue");return "沉默王二";
    }
    
    然后,通过 orElse() 方法和 orElseGet() 方法分别调用 getDefaultValue() 方法返回默认值
    public static void main(String[] args) {
          String name = null;System.out.println("orElse");String name2 = Optional.ofNullable(name).orElse(getDefaultValue());System.out.println("orElseGet");String name3 = Optional.ofNullable(name).orElseGet(OrElseOptionalDemo::getDefaultValue);
    }
    
    类名 :: 方法名是 Java 8 引入的语法,方法名后面是没有 () 的,表明该方法并不一定会被调用
    //结果
    orElse
    getDefaultValueorElseGet
    getDefaultValue
    
    输出结果是相似的,没什么太大的不同,这是在 Optional 对象的值为 null 的情况下。假如 Optional 对象的值不为 null 呢?
    public static void main(String[] args) {
          String name = "沉默王三";System.out.println("orElse");String name2 = Optional.ofNullable(name).orElse(getDefaultValue());System.out.println("orElseGet");String name3 = Optional.ofNullable(name).orElseGet(OrElseOptionalDemo::getDefaultValue);
    }
    
    //结果
    orElse
    getDefaultValueorElseGet
    
    orElseGet() 没有去调用 getDefaultValue()。也就是说,使用 orElseGet() 的性能更好。

5. 获取值

  1. 直观从语义上来看,get() 方法才是最正宗的获取 Optional 对象值的方法,但很遗憾,该方法是有缺陷的,因为假如 Optional 对象的值为 null,该方法会抛出 NoSuchElementException 异常。这完全与我们使用 Optional 类的初衷相悖
    public class GetOptionalDemo {
          public static void main(String[] args) {
          String name = null;Optional<String> optOrNull = Optional.ofNullable(name);System.out.println(optOrNull.get());}
    }
    
    //结果
    Exception in thread "main" java.util.NoSuchElementException: No value presentat java.base/java.util.Optional.get(Optional.java:141)at com.cmower.dzone.optional.GetOptionalDemo.main(GetOptionalDemo.java:9)
    
    尽管抛出的异常是 NoSuchElementException 而不是 NPE,但在我们看来,显然是在“五十步笑百步”。建议 orElseGet() 方法获取 Optional 对象的值。

6. 过滤值

  1. Optional 类的 filter() 方法可以用来过滤值

    public class FilterOptionalDemo {
          public static void main(String[] args) {
          String password = "12345";Optional<String> opt = Optional.ofNullable(password);System.out.println(opt.filter(pwd -> pwd.length() > 6).isPresent());}
    }
    

    filter() 方法的参数类型为 Predicate,也就是说可以将一个 Lambda 表达式传递给该方法作为条件,如果表达式的结果为 false,则返回一个 EMPTY 的 Optional 对象,否则返回过滤后的 Optional 对象

    在上例中,由于 password 的长度为 5 ,所以程序输出的结果为 false。假设密码的长度要求在 6 到 10 位之间,那么还可以再追加一个条件

    Predicate<String> len6 = pwd -> pwd.length() > 6;
    Predicate<String> len10 = pwd -> pwd.length() < 10;password = "1234567";
    opt = Optional.ofNullable(password);
    boolean result = opt.filter(len6.and(len10)).isPresent();
    System.out.println(result); //输出:true
    

7. 转换值

我们可以增加密码的强度,比如说密码不能是“password”,这样的密码太弱了

  1. map() 方法可以按照一定的规则将原有 Optional 对象转换为一个新的 Optional 对象,原有的 Optional 对象不会更改

    public class OptionalMapDemo {
          public static void main(String[] args) {
          String name = "沉默王二";Optional<String> nameOptional = Optional.of(name);Optional<Integer> intOpt = nameOptional.map(String::length);System.out.println( intOpt.orElse(0));}
    }
    

    在上面这个例子中,map() 方法的参数 String::length,意味着要 将原有的字符串类型的 Optional 按照字符串长度重新生成一个新的 Optional 对象,类型为 Integer

    搞清楚了 map() 方法的基本用法后,可以把 map() 方法与 filter() 方法结合起来用,前者用于将密码转化为小写,后者用于判断长度以及是否是“password”

    public class OptionalMapFilterDemo {
          public static void main(String[] args) {
          String password = "PAssworD";Optional<String>  opt = Optional.ofNullable(password);Predicate<String> len6 = pwd -> pwd.length() > 6;Predicate<String> len10 = pwd -> pwd.length() < 10;Predicate<String> eq = pwd -> pwd.equals("password");boolean result = opt.map(String::toLowerCase).filter(len6.and(len10 ).and(eq)).isPresent();System.out.println(result); //输出:true}
    }
    

4. 案例

class Employee {
    private String name;private int age;private double basicSalary;private double dealTotalPrice;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;}public double getBasicSalary() {
    return basicSalary;}public void setBasicSalary(double basicSalary) {
    this.basicSalary = basicSalary;}public double getDealTotalPrice() {
    return dealTotalPrice;}public void setDealTotalPrice(double dealTotalPrice) {
    this.dealTotalPrice = dealTotalPrice;}@Overridepublic String toString() {
    return "Employee{" +"name='" + name + '\'' +", age=" + age +", basicSalary=" + basicSalary +", dealTotalPrice=" + dealTotalPrice +'}';}public Employee(String name, int age, double basicSalary) {
    this.name = name;this.age = age;this.basicSalary = basicSalary;}
}
    public static void main(String[] args) {
    //testOptionalOF(); 抛出异常//testOptionalEmpty(); 抛出异常//testNull(); 抛出异常testOptionalOrElse();}/*** 测试OptionalOF的null*/public static void testOptionalOF() {
    Optional<Employee> optional = Optional.of(null);//不可以用 of 方法创建空对象// 返回 java.lang.NullPointerExceptionSystem.out.println(optional.get());}/*** 测试OptionalEmpty*/public static void testOptionalEmpty() {
    // 构建空的OptionalOptional<Employee> optional = Optional.empty();// 返回 java.util.NoSuchElementException: No value presentSystem.out.println(optional.get());}/*** 测试OptionalOfNull*/public static void testNull() {
    Optional<Employee> optional = Optional.ofNullable(null);// 返回 java.util.NoSuchElementException: No value presentSystem.out.println(optional.get());}/*** 测试OptionalOrelse,map,FlatMap*/public static void testOptionalOrElse() {
    // 创建空的Optional对象Optional<Employee> optional = Optional.ofNullable(null);// 这里因为Optional对象为空,所以将会返回新建的李四对象Employee employee = optional.orElse(new Employee("李四", 25, 20001D));// 打印李四的姓名Optional<String> s = optional.map(Employee::getName);// (e) -> e.getName()Optional<String> s1 = optional.flatMap((e) -> Optional.of(e.getName()));System.out.println(s1);System.out.println(s);System.out.println(employee);Employee employee1 = optional.orElseGet(Employee::new);System.out.println(employee1);}
}//结果
Optional.empty //s1
Optional.empty //s
Employee{
    name='李四', age=25, basicSalary=20001.0, dealTotalPrice=0.0} //employee
Employee{
    name='null', age=0, basicSalary=0.0, dealTotalPrice=0.0} //employee1
  相关解决方案