java8 Optional 使用详解

一、简介

Optional 是一个对象容器,具有以下两个特点:

  • 提示用户要注意该对象有可能为null
    - 简化if else代码

你使用Optional 还这样写代码吗

Optional user = ……
if (user.isPresent()) {
return user.getOrders();
} else {
return Collections.emptyList();
}

如果你还在这样写代码,请认真读完此文:

二、api使用介绍

1. 创建:

empty()

Optional.empty(): 创建一个空的 Optional 实例

    public static void empty() {
        // 创建空的Optional实例
        Optional<String> emptyOptional = Optional.empty();
    }

of()

Optional.of(T t):创建一个 Optional 实例,当 t为null时抛出异常

注意:创建对象时传入的参数不能为null。如果传入参数为null,则抛出NullPointerException 。

    public static void of() {
        // 创建Optional实例:参数非null
        Optional<String> notNullOptional = Optional.of("aaa");

        // 创建Optional实例:参数为null,抛出 NullPointerException
        try {
            Optional<String> nullOptional = Optional.of(null);
        } catch (NullPointerException e) {
            // 输出:null
            System.out.println("【of()】" + e.getMessage());
            // 输出:java.lang.NullPointerException
            e.printStackTrace();
        }
    }

ofNullable()

Optional.ofNullable(T t):创建一个 Optional 实例,但当 t为null时不会抛出异常,而是返回一个空的实例

    public static void ofNullable() {

        // ofNullable()与of()相似,区别在于:ofNullable()是可以接受参数为null的情况。

        // 创建Optional实例:参数非null
        Optional<String> notNullOptional = Optional.ofNullable("aaa");

        // 创建Optional实例:参数为null,不会报错,返回一个空的Optional实例
        Optional<String> nullOptional = Optional.ofNullable(null);
    }

@Test
public void testOptional() {
// 参数不能是null
Optional optional1 = Optional.of(“1”);

// 参数可以是null
Optional optional2 = Optional.ofNullable(null);

// 参数可以是非null
Optional optional3 = Optional.ofNullable(2);
}

2. 获取:

get()

get():获取optional实例中的对象,当optional 容器为空时报错

功能:获取Optional实例值
注意:如果实例值非null,就返回实例值,否则抛出NoSuchElementException

    public static void get() {
        // 获取Optional实例值,如果实例值非null,就返回实例值,否则抛出NoSuchElementException

        // 输出:aaa
        System.out.println("【get()】" + notNullOptional.get());

        try {
            //如果Optional实例值为空,抛出 NoSuchElementException
            // 输出:No value present
            System.out.println("【get()】" + nullOptional.get());
        } catch (NoSuchElementException e) {
            // 输出:java.util.NoSuchElementException
            e.printStackTrace();
        }
    }

3. 判断:

isPresent()

isPresent():判断optional是否为空,如果空则返回false,否则返回true

    public static void isPresent() {
        //检查Optional实例是否包含值:如果值存在返回true,否则返回false
        
        // 输出:true
        System.out.println("【isPresent()】" + notNullOptional.isPresent());
        
        // 输出:false
        System.out.println("【isPresent()】" + nullOptional.isPresent());
    }

ifPresent()

ifPresent(Consumer c):如果optional不为空,则将optional中的对象传给Comsumer函数

    public static void ifPresent() {
        //检查Optional实例是否有值,如果实例值非null,就执行lambda表达式,否则不处理
        notNullOptional.ifPresent(s -> {
          
            System.out.println("【ifPresent()】" + s);
        });

        // 通过ifPresent修改的值,再次通过get获取的时候不会改变
        // 输出:xiaoxian
        System.out.println("【ifPresent()】" + notNullOptional.get());
    }

orElse()

orElse(T other):检查Optional实例是否有值,如果实例非null,就返回实例值,否则返回指定的其它值。

    public static void orElse() {
        //如果Optional实例值非null,返回Optional的值
       
        System.out.println("【orElse()】" + notNullOptional.orElse("There is no value present!"));

        //如果Optional实例值为null,返回传入的值
      
        System.out.println("【orElse()】" + nullOptional.orElse("aaa"));
    }

orElseGet()

orElseGet(Supplier other):如果optional不为空,则返回optional中的对象;如果为null,则使用Supplier函数生成默认值other

    public static void orElseGet() {
        // orElseGet与orElse方法类似
        // 区别在于:orElse传入的是默认值,orElseGet可以接收一个lambda表达式生成默认值

        //如果Optional实例值非null,返回Optional的值
        
        System.out.println("【orElseGet()】" + notNullOptional.orElseGet(() -> "Default Value"));

        //如果Optional实例值为null,返回lambda表达式的值
        // 输出:Default Value
        System.out.println("【orElseGet()】" + nullOptional.orElseGet(() -> "Default Value"));
    }

orElseThrow()

orElseThrow(Supplier exception):如果optional不为空,则返回optional中的对象;如果为null,则抛出Supplier函数生成的异常

    public static void orElseThrow() {
        // orElseThrow与orElse方法类似
        // 区别在于:orElse传入的是默认值,orElseThrow会抛出lambda表达式或方法生成的异常

        try {
            //如果Optional实例值非null,不做任何处理,不会报异常
            notNullOptional.orElseThrow(Exception::new);
        } catch (Exception e) {
            e.printStackTrace();
        }

        try {
            //如果Optional实例值为null,抛异常Exception
            nullOptional.orElseThrow(Exception::new);
        } catch (Throwable e) {
            // 输出: null
            System.out.println("【orElseThrow()】" + e.getMessage());
            e.printStackTrace();
        }
    }

orElse和orElseGet区别
如果optional对象保存的值不是null,则返回原来的值,否则返回value。
orElseGet(Supplier supplier):功能与orElse一样,只不过orElseGet参数是一个对象

//这个示例中,两个 Optional 对象都包含非空值,两个方法都会返回对应的非空值。
// 不过,orElse() 方法仍然创建了 User 对象。
// 与之相反,orElseGet() 方法不创建 User 对象。

@Test
public void test1() {
   User user = new User("23",1);
   log.debug("Using orElse");
   User result = Optional.ofNullable(user).orElse(createNewUser());
   log.info(result.toString());
   log.debug("Using orElseGet");
   User result2 = Optional.ofNullable(user).orElseGet(() -> createNewUser());
   log.info(result2.toString());
}
private User createNewUser() {
   log.debug("Creating New User");
   return new User("123", 1);
}

4. 过滤:filter()

filter(Predicate p):如果有值并且满足条件,就返回该Optional,否则返回空Optional。

/**
 * 值过滤
 */
@Test
public void whenFilter_thenOk() {
   User user = new User("[email protected]", 14);
   Optional<User> result = Optional.ofNullable(user)
         .filter(u -> u.getEmail() != null && u.getEmail().contains("@"));

   assertTrue(result.isPresent());
}

5. 映射:

map()

map(Function<T, U> mapper):如果optional不为空,则将optional中的对象 t 映射成另外一个对象 u,并将 u 存放到一个新的optional容器中。

    public static void map() {
        // 如果Optional实例值非null,执行map(lambda表达式,任意类型),返回新的Optional实例

        Optional<String> notNullToUpperCase = notNullOptional.map((value) -> value.toUpperCase());

     
        System.out.println("【map()】" + notNullToUpperCase.orElse("No value found"));

        Optional<Integer> notNullToInteger = notNullOptional.map((value) -> 1);
        // 输出:1
        System.out.println("【map()】" + notNullToInteger.orElse(2));

        // 如果Optional实例值为null,不用执行map(),返回空Optional
        Optional<String> emptyToUpperCase = nullOptional.map((value) -> value.toUpperCase());
        // 输出:No value found
        System.out.println("【map()】" + emptyToUpperCase.orElse("No value found"));
    }

flatMap()

flatMap(Function< T,Optional> mapper):跟上面一样,在optional不为空的情况下,将对象t映射成另外一个optional

flatMap()与 map()类似,区别在于:传入方法的lambda表达式的返回类型
map():lambda表达式返回值可以是任意类型,在map函数返回之前会包装为Optional
flatMap:lambda表达式返回值必须是Optionl实例,不会把结果包装为Optional

    public static void flatMap() {
        // flatMap()与 map()类似,区别在于:传入方法的lambda表达式的返回类型
        //      map():lambda表达式返回值可以是任意类型,在map函数返回之前会包装为Optional
        //      flatMap:lambda表达式返回值必须是Optionl实例,不会把结果包装为Optional

        //如果Optional的值非null,执行flatMap(lambda表达式),返回Optional类型返回值
        Optional<String> notNullToUpperCase = notNullOptional.flatMap((value) -> Optional.of(value.toUpperCase()));
      
        System.out.println("【flatMap()】" + notNullToUpperCase.orElse("No value found"));

        //如果Optional的值为null,不用执行flatMap(lambda表达式),返回空Optional
        Optional<String> optional = nullOptional.flatMap((value) -> Optional.of(value.toUpperCase()));
        // 输出:No value found
        System.out.println("【flatMap()】" + optional.orElse("No value found"));
    }

@Test
public void map() {
   User user = new User("[email protected]", 21);
   String email = Optional.ofNullable(user)
         .map(u -> u.getName()).orElse("[email protected]");
   log.info(email);
   assertEquals(email, user.getName());
}
@Test
public void flatMap() {
   User user = new User("[email protected]", 23);
   user.setPosition("Developer");
   String position = Optional.ofNullable(user)
         .flatMap(u -> u.getPosition()).orElse("default");
   log.info(position);
   assertEquals(position, user.getPosition().get());
}

如何应用

当我们还在以如下几种方式使用 Optional 时, 就得开始检视自己了

调用 isPresent() 方法时

调用 get() 方法时

Optional 类型作为类/实例属性时

Optional 类型作为方法参数时

Optional 中我们真正可依赖的应该是除了 isPresent() 和 get()的其他方法:

1 public<U> Optional<U> map(Function<? super T, ? extends U> mapper)
2 public T orElse(T other)
3 public T orElseGet(Supplier<? extends T> other)
4 public void ifPresent(Consumer<? super T> consumer)
5 public Optional<T> filter(Predicate<? super T> predicate)
6 public<U> Optional<U> flatMap(Function<? super T, Optional<U>> mapper)
7 public <X extends Throwable> T orElseThrow(Supplier<? extends X> exceptionSupplier) throws X

Optional 的三种构造方式: Optional.of(obj), Optional.ofNullable(obj) 和明确的 Optional.empty();

Optional.of(obj): 它要求传入的 obj 不能是 null 值的, 否则还没开始进入角色就倒在了 NullPointerException 异常上了.

Optional.ofNullable(obj): 它以一种智能的, 宽容的方式来构造一个 Optional 实例. 来者不拒, 传 null 进到就得到 Optional.empty(), 非 null 就调用 Optional.of(obj).

那是不是我们只要用 Optional.ofNullable(obj) ? 那也未必, 否则 Optional.of(obj) 何必暴露呢, 私有则可?

当我们非常非常的明确将要传给 Optional.of(obj) 的 obj 参数不可能为 null 时, 比如它是一个刚 new 出来的对象(Optional.of(new User(…))), 或者是一个非 null 常量时;

当想为 obj 断言不为 null 时, 即我们想在万一 obj 为 null 立即报告 NullPointException 异常, 立即修改, 而不是隐藏空指针异常时, 我们就应该果断的用 Optional.of(obj) 来构造 Optional 实例, 而不让任何不可预计的 null 值有可乘之机隐身于 Optional 中.

现在才开始怎么去使用一个已有的 Optional 实例, 假定我们有一个实例 Optional user, 下面是几个普遍的, 应避免 if(user.isPresent()) { … } else { … }几中应用方式.

存在即返回, 无则提供默认值

1 return user.orElse(null); //而不是 return user.isPresent() ? user.get() : null;
2 return user.orElse(UNKNOWN_USER);
// 推荐写法
User user2 = userOptional.orElse(null);

存在即返回, 无则由函数来产生

1 return user.orElseGet(() -> fetchAUserFromDatabase()); //而不要 return user.isPresent() ? user: fetchAUserFromDatabase();

// 推荐写法
User user4 = userOptional.orElseGet(() -> getUser());

存在才对它做点什么

user.ifPresent(System.out::println);
//而不要下边那样
if (user.isPresent()) {
System.out.println(user.get());
}

// 推荐写法
userOptional.ifPresent(e->getUser());

// 推荐写法
list = userOptional.map(u -> u.getCourseList()).orElse(Collections.emptyList());

map 函数

return user.map(u -> u.getOrders()).orElse(Collections.emptyList())

//上面避免了我们类似 Java 8 之前的做法
if(user.isPresent()) {
  return user.get().getOrders();
} else {
  return Collections.emptyList();
}
return user.map(u -> u.getUsername())
           .map(name -> name.toUpperCase())
           .orElse(null);

一句话小结: 使用 Optional 时尽量不直接调用 Optional.get() 方法, Optional.isPresent() 更应该被视为一个私有方法, 应依赖于其他像 Optional.orElse(), Optional.orElseGet(), Optional.map() 等这样的方法.

四、Demo应用

需求:

学校想从一批学生中,选出年龄大于等于18,参加过考试并且成绩大于80的人去参加比赛。

准备数据

public class Student {
    private String name;
    private int age;
    private Integer score;
    
    //省略 construct get set
}
 
public List<Student> initData(){
    Student s1 = new Student("张三", 19, 80);
    Student s2 = new Student("李四", 19, 50);
    Student s3 = new Student("王五", 23, null);
    Student s4 = new Student("赵六", 16, 90);
    Student s5 = new Student("钱七", 18, 99);
    Student s6 = new Student("孙八", 20, 40);
    Student s7 = new Student("吴九", 21, 88);
 
    return Arrays.asList(s1, s2, s3, s4, s5, s6, s7);
}

java8 之前写法:`@Test
public void beforeJava8() {
List studentList = initData();

for (Student student : studentList) {
    if (student != null) {
        if (student.getAge() >= 18) {
            Integer score = student.getScore();
            if (score != null && score > 80) {
                System.out.println("入选:" + student.getName());
            }
        }
    }
}

}`

java8 写法:

@Test
public void useJava8() {
    List<Student> studentList = initData();
    for (Student student : studentList) {
        Optional<Student> studentOptional = Optional.of(student);
        Integer score = studentOptional.filter(s -> s.getAge() >= 18).map(Student::getScore).orElse(0);
 
        if (score > 80) {
            System.out.println("入选:" + student.getName());
        }
    }
}

猜你喜欢

转载自blog.csdn.net/liuerchong/article/details/119747407#comments_22585144