欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页

浅析Java中的Optional

程序员文章站 2022-03-04 11:10:38
...

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. 参考

Java 8 Optional 类

【java8新特性】Optional详解

深入理解java8中的Optional 类

相关标签: Java