Java8中Optional类的使用说明

目录
  • 简介
    • 历史
    • null带来的种种问题
    • 方案
  • 场景引入
  • 方法说明
    • 构造函数
    • 创建Optional对象
    • 使用map从Optional对象中提取和转换值
    • 使用flatMap链接Optional对象
    • 默认行为及解引用Optional对象1
    • 默认行为及解引用Optional对象2
    • 使用filter剔除特定的值
  • 实战
  • 总结

简介

optional类是java8中引入的针对NPE问题的一种优美处理方式,源码作者也希望以此替代null。

历史

1965年,英国一位名为Tony Hoare的计算机科学家在设计ALGOL W语言时提出了null引用的想法。Hoare选择null引用这种方式,“只是因为这种方法实现起来非常容易”。很多年后,他开始为自己曾经做过这样的决定而后悔不迭,把它称为“我价值百万的重大失误”。我们已经看到它带来的后果——程序员对对象的字段进行检查,判断它的值是否为期望的格式,最终却发现我们查看的并不是一个对象,而是一个空指针,它会立即抛出一个让人厌烦的NullPointerException异常[1]。

null带来的种种问题

  • 错误之源。

NullPointerException是目前Java程序开发中最典型的异常。

  • 代码膨胀。

它让你的代码充斥着深度嵌套的null检查,代码的可读性糟糕透顶。

  • 自身是毫无意义的。

null自身没有任何的语义,尤其是,它代表的是在静态类型语言中以一种错误的方式对缺失变量值的建模。

  • 破坏了Java的哲学。

Java一直试图避免让程序员意识到指针的存在,唯一的例外是:null指针。

  • 在Java的类型系统上开了个口子。

null并不属于任何类型,这意味着它可以被赋值给任意引用类型的变量。这会导致问题,原因是当这个变量被传递到系统中的另一个部分后,你将无法获知这个null变量最初的赋值到底是什么类型。

方案

汲取Haskell和Scala的灵感,Java 8中引入了一个新的类java.util.Optional<T>。这是一个封装Optional值的类。举例来说,使用新的类意味着,如果你知道一个人可能有学校也可能没有,那么Student类内部的school变量就不应该声明为Schoold,遭遇某学生没有学校时把null引用赋值给它,而是应该像本篇那样直接将其声明为Optional<School>类型。

场景引入

首先我们引入一个常见的两个场景

/**
 * >1 引入,常规判断一个学生的学校是不是公立学校,判断是否成年
 */
public static boolean checkIsPublicV1(Student student) {
    if (student != null) {
        School school = student.getSchool();
        if (school != null) {
            return school.isPublicFlag();
        }
    }
    throw new RuntimeException("参数异常");
}

public static String getAdultV1(Student student) {
    if (student != null) {
        int age = student.getAge();
        if (age > 18) {
            return student.getName();
        }
    }
    return "无";
}

上述方式是我们常见的判读流程,optional就是针对每次空判断导致的代码欣赏性问题进行了一套解决方案

方法说明

构造函数

  • Optional(T var1)

源码

private final T value;

private Optional() {
    this.value = null;
}

private Optional(T var1) {
    this.value = Objects.requireNonNull(var1);
}

从源码可知,optional的构造器私有,不能直接创建,只能通过类中的其他静态方法创建,optional构造器支持一个泛型入参,且改参数不能为空

创建Optional对象

  • 声明一个空的Optional: Optional<T> empty()
  • 依据一个非空值创建Optional: Optional<T> of(T var0)
  • 可接受null的Optional: Optional<T> ofNullable(T var0)

源码

empty()源码

private static final Optional<?> EMPTY = new Optional();

private Optional() {
    this.value = null;
}

public static <T> Optional<T> empty() {
    Optional var0 = EMPTY;
    return var0;
}

从源码可知,optional类中维护了一个null值的对象,使用empty静态方法即可返回该空值对象

of(T var0)源码

public static <T> Optional<T> of(T var0) {
    return new Optional(var0);
}

返回常见的有参构造对象,注意由于构造器要求入参不能为空,因此of方法的入参为空的话,依然会报NPE异常

ofNullable(T var0)源码

public static <T> Optional<T> ofNullable(T var0) {
    return var0 == null ? empty() : of(var0);
}

这个方法是对of方法的补强,当ofNullable方法的入参不为空是正常返回构造对象,当入参为空时,返回一个空值的optional对象,而不会抛出异常。

ofNullable方法大部分场景优于of的使用。

null引用和Optional.empty()有什么本质的区别吗?从语义上,你可以把它们当作一回事儿,但是实际中它们之间的差别非常大:如果你尝试解引用一个null,一定会触发NullPointerException,不过使用Optional.empty()就完全没事儿,它是Op-tional类的一个有效对象,多种场景都能调用,非常有用。

举例

public static void testOptionalBuild() {
    // 1. 无法直接new 'Optional()' has private access in 'java.util.Optional'
    // Optional<School> school = new Optional<>();

    // 2. of构建非空和空对象(NullPointerException)
    /*Optional<String> o1 = Optional.of("test");
    System.out.println(o1);
    Optional<Object> o2 = Optional.of(null);
    System.out.println(o2);*/

    // 3. ofNullable构建非空和空对象(Optional.empty)
    /*Optional<String> o3 = Optional.ofNullable("test");
    System.out.println(o3);
    Optional<Object> o4 = Optional.ofNullable(null);
    System.out.println(o4);*/
}

使用map从Optional对象中提取和转换值

  • Optional<U> map(Function<? super T, ? extends U> var1)

源码

public <U> Optional<U> map(Function<? super T, ? extends U> var1) {
    Objects.requireNonNull(var1);
    return !this.isPresent() ? empty() : ofNullable(var1.apply(this.value));
}

当optional包裹的值为空时直接放回空对象,否则执行入参中的Function.apply方法

使用flatMap链接Optional对象

  • Optional<U> flatMap(Function<? super T, Optional<U>> var1)

源码

public <U> Optional<U> flatMap(Function<? super T, Optional<U>> var1) {
    Objects.requireNonNull(var1);
    return !this.isPresent() ? empty() : (Optional)Objects.requireNonNull(var1.apply(this.value));
}

与map几乎一致。注意的是,入参的Function.apply方法中,返回类型为optional类型

举例

public static void testOptionalMap() {
    Student student1 = getDefaultStudent();
    Student student2 = getBackStudent();
    // map
    String school1 = Optional.ofNullable(student1).map(i -> i.getName()).orElse("无名");
    String school2 = Optional.ofNullable(student2).map(i -> i.getName()).orElse("无名");
    System.out.println("school1: " + school1 + "| school2: " + school2);
    // flapMap 链式
    String school3 = Optional.ofNullable(getOptionalStudent()).flatMap(i -> getOptionalStudent()).flatMap(i->i.getSchool()).map(i->i.getSchoolName()).orElse("没上大学");
    System.out.println("school3: " + school3);
}

默认行为及解引用Optional对象1

  • T orElse(T var1)
  • T orElseGet(Supplier<? extends T> var1)
  • T orElseThrow(Supplier<? extends X> var1)

注:这三个方法方法不是静态方法,因此需要通过实例对象调用,一般跟在方法ofNullable后用于处理空值或返回值

源码

orElse源码

public T orElse(T var1) {
    return this.value != null ? this.value : var1;
}

当optional中的包裹值不为空时返回包裹的值,若为空则返回orElse中的入参值

orElseGet源码

public T orElseGet(Supplier<? extends T> var1) {
    return this.value != null ? this.value : var1.get();
}
public T get() {
    if (this.value == null) {
        throw new NoSuchElementException("No value present");
    } else {
        return this.value;
    }
}

与上个方法类似,当optional中的包裹值不为空时返回包裹的值,若为空执行orElseGet中的Supplier方法

orElseThrow源码

public <X extends Throwable> T orElseThrow(Supplier<? extends X> var1) throws X {
    if (this.value != null) {
        return this.value;
    } else {
        throw (Throwable)var1.get();
    }
}

类似的,当optional中的包裹值不为空时返回包裹的值,若为空抛出orElseThrow中的Supplier.get的异常方法

举例

public static void testOptionalOrElse() {
    // orElse
    Student stu = getDefaultStudent();
    Student backStudent = getBackStudent();
    Student realStu1 = Optional.ofNullable(stu).orElse(backStudent);
    System.out.println(realStu1);

    // orElseGet
    Student realStu2 = Optional.ofNullable(stu).orElseGet(()-> getBackStudent());
    System.out.println(realStu2);

    // orElseGet
    Student realStu3 = Optional.ofNullable(stu).orElseThrow(()-> new RuntimeException("学生不存在"));
    System.out.println(realStu3);
}

默认行为及解引用Optional对象2

  • boolean isPresent()
  • void ifPresent(Consumer<? super T> var1)

源码

isPresent()源码
public boolean isPresent() {
    return this.value != null;
}

用户判断optional包裹的值是否为空,返回布尔值

ifPresent(Consumer var1)源码

public void ifPresent(Consumer<? super T> var1) {
    if (this.value != null) {
        var1.accept(this.value);
    }
}

用户处理optional包裹的值不为空时,继续处理入参中Consumer.accept的方法。类似于 if(var!=null) {do sth}

举例

public static void testOptionalIfPresent() {
    // isPresent()
    Student student1 = getDefaultStudent();
    Student student2 = getBackStudent();
    boolean b1 = Optional.ofNullable(student1).isPresent();
    boolean b2 = Optional.ofNullable(student2).isPresent();
    System.out.println("b1: " + b1 + "| b2: " + b2);

    // isPresent(Consumer)
    Optional.ofNullable(student2).ifPresent(i-> acceptStudent(i, LocalDate.now()));
}

使用filter剔除特定的值

  • Optional filter(Predicate<? super T> var1)

源码

public Optional<T> filter(Predicate<? super T> var1) {
    Objects.requireNonNull(var1);
    if (!this.isPresent()) {
        return this;
    } else {
        return var1.test(this.value) ? this : empty();
    }
}

用于对optional对象的过滤,当optional包裹的值不为空时返回该值,否则执行filter入参的Predicate.test方法

举例

public static void testOptionalFilter() {
    Student student1 = getDefaultStudent();
    Student student2 = getBackStudent();
    System.out.println(student1);
    System.out.println(student2);
    Student student3 = Optional.ofNullable(student1).filter(i -> i.getAge() > 18).orElse(getBackStudent());
    Student student4 = Optional.ofNullable(student2).filter(i -> i.getAge() > 18).orElse(getBackStudent());
    System.out.println(student3);
    System.out.println(student4);
}

实战

关于optional类的说明大致已经讲完,再回到开始的时候,提到的场景引入,结合optional进行改造

/**
 * 实战1
 * 针对原来的checkIsPublicV1进行改造
 */
public static boolean checkIsPublicV2(Student student) {
    return Optional.ofNullable(student).map(i -> i.getSchool()).map(i -> i.isPublicFlag()).orElseThrow(() -> new RuntimeException("参数异常"));
}

/**
 * 实战1
 * 针对原来的getAdultV1进行改造
 */
public static String getAdultV2(Student student) {
    return Optional.ofNullable(student).filter(i->i.getAge()>18).map(i->i.getName()).orElseGet(()->getDefaultStudent().getName());
}

附:

补充代码

public static void main(String[] args) {
    //逐个放开
    // 引入
//        System.out.println(checkIsPublicV1(stu2));
//        System.out.println(getAdultV1(stu2));
    // optional方法
//        testOptionalBuild();
//        testOptionalOrElse();
//        testOptionalIfPresent();
//        testOptionalMap();
//        testOptionalFilter();
    // 实战
//        System.out.println(getAdultV2(stu3));
//        System.out.println(checkIsPublicV2(stu3));
}

/**========模型数据=======**/
@Data
@Builder
@NoArgsConstructor
@AllArgsConstructor
static class Student {
    private String name;
    private int age;
    private School school;
}

@Data
@Builder
@NoArgsConstructor
@AllArgsConstructor
static class School {
    private String schoolName;
    private boolean publicFlag;
}

@Data
@Builder
@NoArgsConstructor
@AllArgsConstructor
static class StudentOpt {
    private String name;
    private int age;
    private Optional<School> school;
}

public static Student getDefaultStudent() {
    return null;
}

public static Student getBackStudent() {
    return Student.builder().name("小红").age(19).build();
}

public static Optional<StudentOpt> getOptionalStudent() {
    return Optional.ofNullable(StudentOpt.builder().name("小莫").age(18)
            .school(Optional.ofNullable(School.builder().schoolName("蓝鲸大学").publicFlag(true).build())).build());
}

public static void acceptStudent(Student stu, LocalDate date) {
    System.out.println("日期: " + date + " 新增一位学生: " + stu.getName());
}

[1] 参考自java8实战

详细源码,请参考:github.com/chetwhy/clo

总结

到此这篇关于Java8中Optional类使用的文章就介绍到这了,更多相关Java8 Optional类使用内容请搜索我们以前的文章或继续浏览下面的相关文章希望大家以后多多支持我们!

(0)

相关推荐

  • 详解JAVA中的OPTIONAL

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

  • Java JDK8新增Optional工具类讲解

    Optional 空指针异Optional常是导致Java应用程序失败的最常见原因.以前,为了解决空指针异常,Google公司著名的Guava项目引入了Optional类,Guava通过使用检查空值的方式来防止代码污染,它鼓励程序员写更干净的代码.受到Google Guava的启发,Optional类已经成为Java 8类库的一部分.Optional实际上是个容器:它可以保存类型T的值,或者仅仅保存null.Optional提供很多有用的方法,这样我们就不用显式进行空值检测. 1. 以前对nul

  • JDK8中Optional类巧用之判空操作

    前言 相信大家肯定所有的开发者都对Java8里面的所有的东西都感兴趣,虽然目前的 JDK 已经更新到 JDK17 了,但是我相信,现在很多公司使用的还都是 JDK8 甚至是 JDK7,但是,就算是有些公司已经升级到 JDK8 但是对于 JDK8 里面的一些东西的使用,却没有使用的淋漓尽致. 今天就给大家放出几个 JDK8 里面比较好用的. JDK8 大家都是知道 JDK8 就开始使用 Lambda 表达式,但是很多不管是在教程上还是在其他的书籍上,都没有说在实际开发的时候去使用上这个 Lambd

  • Java关于JDK1.8中的Optional类

    目录 Java关于JDK1.8中的Optional类 一.定义 二.Optional方法的使用 1.empty 方法 2.of 方法返 3.返回一个Optional实例 4.isPresent 方法 5.get方法 6.ifPresent 7.filter方法 8. map(Function) 9.flatMap() 10.orElse方法 11.orElseGet(Supplier supplier) 12.orElseThrow方法 三.实战参考 Java关于JDK1.8中的Optional

  • JDK8中新增的Optional工具类基本使用

    Optional类的使用 JDK8以前,编写代码,通常会出现 NullPointerException (空指针异常),通常情况下我们都是通过 if ... else... 来对对象进行为空判断,然后再进行逻辑处理,代码写起来也比较冗余. JDK8新增了Optional类,使用该类可以避免我们对空指针的检查,使代码看起来比较优雅.   最近刚好有空给大家整理下JDK8的特性,这个在实际开发中的作用也是越来越重了,本文重点讲解下Optional 这个Optional类注意是解决空指针的问题 1.

  • JAVA8妙用Optional解决判断Null为空的问题方法

    目录 引言 API介绍 1.Optional(T value),empty(),of(T value),ofNullable(T value) 2.orElse(T other),orElseGet(Supplier<? extends T> other)和orElseThrow(Supplier<? extends X> exceptionSupplier) 3.map(Function<? super T, ? extends U> mapper)和flatMap(

  • 深入理解Java8新特性之Optional容器类的应用

    1.Optional容器类 Optional<T> 类(java.util.Optional) 是一个容器类,代表一个值存在或不存在,原来用 null 表示一个值不存在,现在 Optional 可以更好的表达这个概念.并且可以避免空指针异常. 常用方法 : Optional.of(T t) : 创建一个 Optional 实例 Optional.empty() : 创建一个空的 Optional 实例 Optional.ofNullable(T t) : 若 t 不为 null,创建 Opti

  • JDK1.8新特性之方法引用 ::和Optional详解

    一:简介 方法引用分为三种,方法引用通过一对双冒号:: 来表示,方法引用是一种函数式接口的另一种书写方式 静态方法引用,通过类名::静态方法名, 如 Integer::parseInt 实例方法引用,通过实例对象::实例方法,如 str::substring 构造方法引用,通过类名::new, 如 User::new 二:方法引用 public final class Integer { public static int parseInt(String s) throws NumberForm

  • Java8中Optional类的使用说明

    目录 简介 历史 null带来的种种问题 方案 场景引入 方法说明 构造函数 创建Optional对象 使用map从Optional对象中提取和转换值 使用flatMap链接Optional对象 默认行为及解引用Optional对象1 默认行为及解引用Optional对象2 使用filter剔除特定的值 实战 总结 简介 optional类是java8中引入的针对NPE问题的一种优美处理方式,源码作者也希望以此替代null. 历史 1965年,英国一位名为Tony Hoare的计算机科学家在设计

  • Java8中Optional类型和Kotlin中可空类型的使用对比

    本文主要给大家介绍了关于Java8中Optional类型和Kotlin中可空类型使用的相关内容,分享出来供大家参考学习,下面话不多说了,来一起看看详细的介绍: 在 Java 8中,我们可以使用 Optional 类型来表达可空的类型. package com.easy.kotlin; import java.util.Optional; import static java.lang.System.out; /** * Optional.ofNullable - 允许传递为 null 参数 *

  • Java8中Optional操作的实际应用

    目录 简介 正文 1. Optional是什么 2. 没它 VS 有它 3. 核心操作 4. 应用 总结 总结 简介 目的:Optional的出现主要是为了解决null指针问题,也叫NPE(NullPointerException) 外形:Optional外形酷似容器(其实它就是一个容器),只是这个容器比较特殊,因为它只能存放一个对象,运气不好的话这个对象还是个null 操作:Optional从操作上来看,又跟前面的Stream流式操作很像,比如过滤filter - 提取map等 下面我们用比较

  • 详解Java中Optional类的使用方法

    目录 一.Optional类的来源 二.Optional类是什么 三.Optional类用法 四.代码示例 1.创建Optional类 2.判断Optional容器中是否包含对象 3.获取Optional容器的对象 4.过滤 5.映射 五.什么场景用Optional 1.场景一 2.场景二 3.场景三 4.场景四 一.Optional类的来源 到目前为止,臭名昭著的空指针异常是导致Java应用程序失败的最常见原因.以前,为了解决空指针异常,Google公司著名的Guava项目引入了Optiona

  • Java中Optional类及orElse方法详解

    目录 引言 Java 中的 Optional 类 ofNullable() 方法 orElse() 方法 案例 orElseGet() 方法 案例 orElse() 与 orElseGet() 之间的区别 引言 为了让我更快的熟悉代码,前段时间组长交代了一个小任务,大致就是让我整理一下某个模块中涉及的 sql,也是方便我有目的的看代码,也是以后方便他们查问题(因为这个模块,涉及的判断很多,所以之前如果 sql 出错了,查问题比较繁琐). 昨天算是基本完成了,然后今天组长就让给我看一个该模块的缺陷

  • 详解Java8中Optional的常见用法

    目录 一. 简介 二.Java8 之前,空指针异常判断 三.Optional的使用 1.创建Optional实例 2.访问 Optional 对象的值 3.返回默认值 4.返回异常 (常用) 5.转换值 6.过滤值 一. 简介 Opitonal是java8引入的一个新类,目的是为了解决空指针异常问题.本质上,这是一个包含有可选值的包装类,这意味着 Optional 类既可以含有对象也可以为空. Optional 是 Java 实现函数式编程的强劲一步,并且帮助在范式中实现.但是 Optional

  • 使用Java8中Optional机制的正确姿势

    前言 Java8带来的函数式编程特性对于习惯命令式编程的程序员来说还是有一定的障碍的,我们只有深入了解这些机制的方方面面才能运用自如.Null的处理在JAVA编程中是出了try catch之外的另一个头疼的问题,需要大量的非空判断模板代码,程序逻辑嵌套层次太深.尤其是对集合的使用,需要层层判空. 首先来看下Optional类的结构图: 而如果我们对它不稍假探索, 只是轻描淡写的认为它可以优雅的解决 NullPointException 的问题, 于是代码就开始这么写了 Optional<User

  • Java8中Optional的一些常见错误用法总结

    前言 Java 8 引入的 Optional 类型,基本是把它当作 null 值优雅的处理方式.其实也不完全如此,Optional 在语义上更能体现有还是没有值.所以它不是设计来作为 null 的替代品,如果方法返回 null 值表达了二义性,没有结果或是执行中出现异常. 在 Oracle  做  Java 语言工作的  Brian Goetz 在 Stack Overflow 回复 Should Java 8 getters return optional type? 中讲述了引入  Opti

  • java中Optional的使用详细解析

    Optional的使用详解 1.Optional介绍 Optional 类是一个可以为null的容器对象.如果值存在则isPresent()方法会返回true,调用get()方法会返回该对象. Optional 是个容器:它可以保存类型T的值,或者仅仅保存null.Optional提供很多有用的方法,这样我们就不用显式进行空值检测. Optional 类的引入很好的解决空指针异常. 2.构建Optional 构建一个Optional对象:方法有:empty( ).of( ).ofNullable

随机推荐