首页 > 编程语言 >[Java SE]Java 8特性:java.util.Optional

[Java SE]Java 8特性:java.util.Optional

时间:2022-12-29 13:11:07浏览次数:63  
标签:java name util log Java null Optional public String

1 概述

本质上,这是一个包含有可选值的包装类,这意味着 Optional 类既可以含有对象也可以为空(null/empty)
OptionalJava 实现函数式编程的强劲一步,并且帮助在范式中实现。但是 Optional 的意义显然不止于此。
我们从一个简单的用例开始。在 Java 8 之前,任何访问对象方法或属性的调用都可能导致NullPointerException

String isocode = user.getAddress().getCountry().getIsocode().toUpperCase();

在这个小示例中,如果我们需要确保不触发异常,就得在访问每一个值之前对其进行明确地检查:

if (user != null) {
    Address address = user.getAddress();
    if (address != null) {
        Country country = address.getCountry();
        if (country != null) {
            String isocode = country.getIsocode();
            if (isocode != null) {
                isocode = isocode.toUpperCase();
            }
        }
    }
}

2 样例源码

你看到了,这很容易就变得冗长,难以维护。

为了简化这个过程,我们来看看用 Optional 类是怎么做的。从创建和验证实例,到使用其不同的方法,并与其它返回相同类型的方法相结合,下面是见证Optional 奇迹的时刻。

WithoutOptionalTest

package test.java.util;

import lombok.extern.slf4j.Slf4j;
import org.junit.Assert;
import org.junit.Test;

/**
 * @create-time 2022/12/29 11:00
 * @description 没有 Optional 会有什么问题?
 *  我们来模拟一个实际的应用场景。
 *  小王第一天上班,领导老马就给他安排了一个任务,要他从数据库中根据会员 ID 拉取一个会员的姓名,然后将姓名打印到控制台。
 *  虽然是新来的,但这个任务难不倒小王,于是他花了 10 分钟写下了本段代码(WithoutOptionalTest)
 * @reference-doc
 *  [1] 干货,一文彻底搞懂 Java 的 Optional - CSDN - https://blog.csdn.net/qing_gee/article/details/104767082
 */
@Slf4j
public class WithoutOptionalTest {
    /**
     * 由于当前 ID 的会员不存在,故 getMemberByIdFromDB() 返回了 null 来作为没有获取到该会员的结果。
     * 意味着:在打印会员姓名的时候要先对 mem 判空;否则,就会抛出 NPE 异常。
     * 不信?让小王把 if (mem != null) 去掉试试,控制台立马打印错误堆栈给你颜色看看。
     */
    @Test
    public void withoutOptionalTest(){
        Member mem = getMemberByIdFromDB();
        if (mem != null) {
            log.info("member.name: {}", mem.getName());
        }
        Assert.assertTrue(true);
    }

    public static Member getMemberByIdFromDB() {
        // DB查询结果:当前 ID 的会员不存在
        return null;
    }

    class Member {
        private String name;

        public String getName() {
            return name;
        }

        public void setName(String name) {
            this.name = name;
        }
    }
}

OptionalTest

package test.java.util;

import lombok.extern.slf4j.Slf4j;
import org.junit.Assert;
import org.junit.Test;

import java.util.Collections;
import java.util.List;
import java.util.Optional;

/**
 * @create-time 2022/12/29 10:59
 * @description Optional 是如何解决这个问题的?
 *  小王把代码提交后,就兴高采烈地去找老马要新的任务了。
 *  本着虚心学习的态度,小王请求老马看一下自己的代码,于是老王就告诉他应该尝试一下 Optional,可以避免没有必要的 null 值检查。
 *  现在,让我们来看看小王是如何通过 Optional 来解决上述问题的。
 * @reference-doc
 *  [1] 干货,一文彻底搞懂 Java 的 Optional - CSDN - https://blog.csdn.net/qing_gee/article/details/104767082
 */
@Slf4j
public class OptionalTest {
    /**
     * getMemberByIdFromDB() 方法返回了 Optional<Member> 作为结果,这样就表明 Member 可能存在,也可能不存在,这时候就可以:
     *     在 Optional 的 ifPresent() 方法中使用 Lambda 表达式来直接打印结果。
     *
     * Optional 之所以可以解决 NPE 的问题,是因为:它明确的告诉我们,不需要对它进行判空。它就好像十字路口的路标,明确地告诉你该往哪走。
     */
    @Test
    public void withOptionalTest(){
        Optional<Member> optional = getMemberByIdFromDB();
        optional.ifPresent(mem -> {
            log.debug("member.name: {}", mem.getName());
        });
        log.debug("optional.isPresent : {}", optional.isPresent());
        log.debug("optional.get() : {}", optional.get());

        optional = Optional.empty();
        Member member = optional.orElse(new Member("optional 为空时设置的默认会员信息"));
        log.debug("member : {}", member);
        //如果存在该值,返回包含的值,否则抛出由 Supplier 继承的异常
        //optional.orElseThrow(() -> { return new RuntimeException("optional为空时出现异常!"); });

        Assert.assertTrue(true);
    }

    /**
     * ofNullable() 方法内部有一个三元表达式:
     *  如果为参数为 null,则返回私有常量 EMPTY;
     *  否则,使用 new 关键字创建了一个新的 Optional 对象——不会再抛出 NPE 异常了。
     */
    @Test
    public void ofNullableTest(){
        //true
        List<Member> list = Collections.EMPTY_LIST;
        Optional<List<Member>> optionalList = Optional.ofNullable(list);
        boolean present = optionalList.isPresent();
        log.debug("present : {}", present);
        Assert.assertTrue(true);
    }

    @Test
    public void createOptionalObject01(){
        //可以使用静态方法 empty() 创建一个空的 Optional 对象
        Optional<String> empty = Optional.empty();
        log.debug(empty.toString()); // 输出:Optional.empty

        String str = null;
        Optional<String> optionalStr = Optional.ofNullable(str);
        boolean isPresent2 = optionalStr.isPresent();
        log.debug("optionalStr : {}", optionalStr); // 输出:Optional.empty
        log.debug("isPresent2 : {}", isPresent2); // 输出:false

        Assert.assertTrue(true);
    }

    @Test
    public void createOptionalObject02(){
        //可以使用静态方法 of() 创建一个非空的 Optional 对象
        Optional<String> opt = Optional.of("沉默王二");
        log.debug(opt.toString()); // 输出:Optional[沉默王二]

        //当然了,传递给 of() 方法的参数必须是非空的,也就是说不能为 null,否则仍然会抛出 NullPointerException。
//        String name = null;
//        Optional<String> optnull = Optional.of(name);// java.lang.NullPointerException

        Assert.assertTrue(true);
    }

    @Test
    public void createOptionalObject03(){
        String name = null;
        Optional<String> optOrNull = Optional.ofNullable(name);
        log.debug("optOrNull : {}", optOrNull); // 输出:Optional.empty

        Assert.assertTrue(true);
    }

    @Test
    public void OrElseTest(){
        // 有时候,我们在创建(获取) Optional 对象的时候,需要一个默认值,orElse() 和 orElseGet() 方法就派上用场了。
        // orElse() 方法用于返回包裹在 Optional 对象中的值,如果该值不为 null,则返回 原本的值;否则返回默认值。该方法的参数类型和值得类型一致。
        String nullName = null;
        String name = Optional.ofNullable(nullName).orElse("沉默王二");
        log.debug("name : {}", name); // 输出:沉默王二

        String nullName2 = "hello";
        String name2 = Optional.ofNullable(nullName2).orElse("沉默王二");
        log.debug("name2 : {}", name2); // 输出:hello

        Assert.assertTrue(true);
    }

    @Test
    public void orElseGetTest(){
        //orElseGet() 方法与 orElse() 方法类似,但参数类型不同。如果 Optional 对象中的值为 null,则执行参数中的函数。
        String nullName = null;
        String name = Optional.ofNullable(nullName).orElseGet(()-> { return "沉默王二"; });
        log.debug("name : {}", name); // 输出:沉默王二

        Assert.assertTrue(true);
    }

    public static Optional<Member> getMemberByIdFromDB() {
        boolean hasName = true;
        if (hasName) {
            return Optional.of(new Member("沉默王二"));
        }
        return Optional.empty();
    }

}

class Member {
    private String name;

    public Member(String name) {
        this.name = name;
    }

    public String getName() {
        return name;
    }

    // getter / setter

    @Override
    public String toString() {
        return "Member{" +
                "name='" + name + '\'' +
                '}';
    }
}

X 参考文献

标签:java,name,util,log,Java,null,Optional,public,String
From: https://www.cnblogs.com/johnnyzen/p/17012273.html

相关文章

  • 第二章《Java程序世界初探》第7节:关系运算符及条件语句
    ​关系运算符有==、!=、>、>=、<、<=。它们分别表示等于、不等于、大于、大于等于、小于和小于等于。关系运算符的作用是判断两个数据之间是否存在某种逻辑关系。既然是“判......
  • 第二章《Java程序世界初探》第5节:算术运算符
    在学习本小节内容之前,各位读者必须先理解几个概念:运算符、操作数和表达式。所谓“运算符”就是指具有一定运算意义的符号,例如+、-都是运算符。根据运算符完成运算所需数据量......
  • 第二章《Java程序世界初探》第6节:赋值运算符
    ​赋值运算符可以分为两类:普通赋值运算符和复合赋值运算符。普通赋值运算符只能起到简单的赋值作用,而复合赋值运算符则是先完成一次其他类型的运算然后再进行赋值操作。本小......
  • Triple 协议支持 Java 异常回传的设计与实现
    作者:ApacheDubboContributor陈景明背景在一些业务场景,往往需要自定义异常来满足特定的业务,主流用法是在catch里抛出异常,例如:publicvoiddeal(){try{......
  • 跟光磊学Java-Windows版IntelliJ IDEA安装和卸载
    IntelliJIDEA  安装、获得许可后才能使用IntelliJIDEA,如果不想使用IntelliJIDEA那么又应该如何卸载呢?1.IntelliJIDEA下载访问JetBrains官网,访问地......
  • 跟光磊学Java-macOS版IntelliJ IDEA设置
    IntelliJIDEA  在使用IntelliJIDEA开发项目之前还需要进行一些基本的设置,完成这些设置以后能让项目开发更加顺畅1.如何打开设置窗口在进行IntelliJIDE......
  • Triple 协议支持 Java 异常回传的设计与实现
    作者:ApacheDubboContributor陈景明背景在一些业务场景,往往需要自定义异常来满足特定的业务,主流用法是在catch里抛出异常,例如:publicvoiddeal(){try{......
  • 【Java】Tips
    GET还是POST?GET比POST更简单更快,可用于大多数情况下。不过,请在以下情况始终使用POST:缓存文件不是选项(更新服务器上的文件或数据库)向服务器发送大量数据(POST无大......
  • 跟光磊学Java-macOS版IntelliJ IDEA开发Java项目
    IntelliJIDEA  配置IntelliJIDEA后,就可以用它开发Java项目了,这里会手把手带领大家从头开始开发一个多模块的java项目1.IntelliJIDEA开发Java项目的流程......
  • 第二章《Java程序世界初探》第1节:认识Java语言的变量
    ​如果编写一个求绝对值的程序,开发者可以把程序设计为由用户从控制台上任意输入一个数字,然后计算这个数字的绝对值并输出到控制台上。为了完成这个操作,需要先向计算机申请一......