首页 > 编程语言 >java.util.Optional基础

java.util.Optional基础

时间:2022-12-21 09:12:24浏览次数:50  
标签:java car util person return null Optional public

传统处理Null方式

深层质疑

每次不确定一个变量是否为null时,添加一个进一步嵌套的if块,增加了代码缩进的层数。

public String getCarInsuranceName(Person person) {
    if (person != null) {
        Car car = person.getCar();
        if (car != null) {
            Insurance insurance = car.getInsurance();
            if (insurance != null) {
                return insurance.getName();
            }
        }
    }
    return "Unknown";

}

多路退出

每次遭遇null变量,都返回一个字符串常量“Unknown”。

public String getCarInsuranceName2(Person person) {
    if (person == null) {
        return "Unknown";
    }
    Car car = person.getCar();
    if (car == null) {
        return "Unknown";
    }
    Insurance insurance = car.getInsurance();
    if (insurance == null) {
        return "Unknown";
    }
    return insurance.getName();
}

Optional 类

变量存在时,Optional类只是对类简单封装。变量不存在时,缺失的值会被建模成一个“空”的Optional对象,由方法Optional.empty()返回。Optional.empty()方法是一个静态工厂方法,它返回Optional类的特定单一实例。

Optional类重构

public class Person {
    private Optional<Car> car;
    public Optional<Car> getCar() { return car; }
}
public class Car {
    private Optional<Insurance> insurance;
    public Optional<Insurance> getInsurance() { return insurance; }
}
public class Insurance {
    private String name;
    public String getName() { return name; }
}

使用Optional丰富了语义,说明了可能为空的情况。但是保险insurance必须有string名字(这属于业务层面的判断)

应用

创建对象

声明一个空的Optional

Optional<Car> optCar = Optional.empty();

依据一个非空值创建Optional

Optional<Car> optCar = Optional.of(car);

如果car是一个null,这段代码会立即抛出一个NullPointerException,而不是等到你试图访问car的属性值时才返回一个错误。

可接受null的Optional

Optional<Car> optCar = Optional.ofNullable(car);

如果car是null,那么得到的Optional对象就是个空对象。

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

Optional<Insurance> optInsurance = Optional.ofNullable(insurance);
Optional<String> name = optInsurance.map(Insurance::getName);

使用 flatMap 链接 Optional 对象

使用Optional获取car的保险公司名称

public String getCarInsuranceName(Optional<Person> person) {
    return person.flatMap(Person::getCar)
                 .flatMap(Car::getInsurance)
                 .map(Insurance::getName)
                 .orElse("Unknown");
}

使用orElse的方法,当Optional的值为空时,它会为其设定一个默认值

使用 filter 剔除特定的值

如果Optional对象的值存在,并且它符合谓词的条件,filter方法就返回其值;否则它就返回一个空的Optional对象。

Optional<Insurance> optInsurance = ...;
optInsurance.filter(insurance ->
            	"CambridgeInsurance".equals(insurance.getName()))
            .ifPresent(x -> System.out.println("ok"));

public static String getCarInsuranceName(Optional<Person> person, int minAge) {
    return person.filter(p -> p.getAge() >= minAge)
                .flatMap(Person::getCar)
                .flatMap(Car::getInsurance)
                .map(Insurance::getName)
                .orElse("Unknown");
}

多种方法读取Optional实例中的变量值

get():如果变量存在,它直接返回封装的变量值,否则就抛出一个NoSuchElementException异常

orElse(T other):允许在Optional对象不包含值时提供一个默认值

orElseGet(Supplier<? extends T> other):orElse方法的延迟调用版,Supplier方法只有在Optional对象不含值时才执行调用

orElseThrow(Supplier<? extends X> exceptionSupplier):使用orElseThrow可以定制希望抛出的异常类型

ifPresent(Consumer<? super T>):在变量值存在时执行一个作为参数传入的方法,否则就不进行任何操作

两个 Optional 对象的组合示例

找到满足该组合的最便宜的保险公司

public Insurance findCheapestInsurance(Person person, Car car) {
    // 不同的保险公司提供的查询服务
    // 对比所有数据
    return cheapestCompany;
}

传统判空

public Optional<Insurance> nullSafeFindCheapestInsurance (
        Optional<Person> person, Optional<Car> car){
    if (person.isPresent() && car.isPresent()) {
        return Optional.of(findCheapestInsurance(person.get(), car.get()));
    } else {
        return Optional.empty();
    }
}

一行实现

public Optional<Insurance> nullSafeFindCheapestInsurance(
        Optional<Person> person, Optional<Car> car) {
    return person.flatMap(p -> car.map(c -> findCheapestInsurance(p, c)));
}

这段代码中,你对第一个Optional对象调用flatMap方法,如果它是个空值,传递给它的Lambda表达式不会执行,这次调用会直接返回一个空的Optional对象。反之,如果person对象存在,这次调用就会将其作为函数Function的输入,并按照与flatMap方法的约定返回一个Optional<Insurance>对象。

这个函数的函数体会对第二个Optional对象执行map操作,如果第二个对象不包含car,函数Function就返回一个空的Optional对象,整个nullSafeFindCheapestInsuranc方法的返回值也是一个空的Optional对象。

最后,如果person和car对象都存在,作为参数传递给map方法的Lambda表达式能够使用这两个值安全地调用原始的findCheapestInsurance方法,完成期望的操作。

Optional 的实战示例

封装可能为 null 的值

使用Optional封装map的返回值

Optional<Object> value = Optional.ofNullable(map.get("key"));

异常与 Optional 的对比

由于某种原因,函数无法返回某个值,这时除了返回null,Java API比较常见的替代做法是抛出一个异常。

public static Optional<Integer> stringToInt(String s){
    try {
        return Optional.of(Integer.parseInt(s));
    } catch (NumberFormatException e) {
        return Optional.empty();
    }
}

我们的建议是,你可以将多个类似的方法封装到一个工具类中,让我们称之为OptionalUtility。通过这种方式,你以后就能直接调用OptionalUtility.stringToInt方法,将String转换为一个Optional<Integer>对象,而不再需要记得你在其中封装了笨拙的try/catch的逻辑了。

完整示例

package Optional;

import java.util.Optional;
import java.util.Properties;

public class OptionalTest2 {
    public static void main(String[] args) {
        Properties props = new Properties();
        props.setProperty("a", "5");
        props.setProperty("b", "true");
        props.setProperty("e", "false");
        props.setProperty("c", "-3");

        int re = readDuration2(props, "c");
        System.out.println("re = " + re);

    }

    public static int readDuration2(Properties props, String name) {
        return Optional.ofNullable(props.getProperty(name))
                .flatMap(OptionalTest2::stringToInt)
                .filter(i -> i > 0)
                .orElse(0);
    }

    public static int readDuration(Properties props, String name) {
        String value = props.getProperty(name);
        if (value != null) {
            try {
                int i = Integer.parseInt(value);
                if (i > 0) {
                    return i;
                }
            } catch (NumberFormatException ignored) {
            }
        }
        return 0;
    }

    public static Optional<Integer> stringToInt(String s) {
        try {
            return Optional.of(Integer.parseInt(s));
        } catch (NumberFormatException e) {
            return Optional.empty();
        }
    }

}

标签:java,car,util,person,return,null,Optional,public
From: https://www.cnblogs.com/ancientlian/p/16995496.html

相关文章

  • Java学习笔记2
    1.运算符和表达式运算符​ 就是对常量或者变量进行操作的符号。​ 如:+-*/表达式​ 用运算符把常量或者变量连接起来的,符合Java语法的式子就是表达式。​ 如:a+b......
  • Java 如何将输入的一组数,加某一符号后输出?如何以某一字符隔开的形式输入?
    以逗号进行举例以逗号分开输出:Stringstr=“zxc”;ArrayList<String> list=new Arraylist<>();for(inti=0;i<str.length;i++)list.add(str.CharAt(i)+"");String ......
  • JAVA常见算法
    packagecom.example.cesium.utils;publicclassdemo{/***二查分算法*半查找,要求待查找的序列有序。每次取中间位置的值与待查关键字比较,如果中......
  • 吃透JAVA的Stream流操作,多年实践总结
    在JAVA中,涉及到对数组、Collection等集合类中的元素进行操作的时候,通常会通过循环的方式进行逐个处理,或者使用Stream的方式进行处理。例如,现在有这么一个需求:从给定句子......
  • Python Type Hint中Optional[str]=None和str=None的区别
    PythonTypeHint中Optional[str]=None和str=None的区别1问题来源在读到FluentPython,2edEdition,P260时产生了一些疑问:在Python中使用typehint时,以下两个声明有......
  • java oop
    1.类与对象类是一个模板:抽象,对象是一个具体的实例2.方法定义,调用:3.对象的引用引用类型:基本类型(8)对象是通过引用来操作的:栈-->堆4.属性:字段Field成员变量默认初始......
  • Java多线程批量操作,居然有人不做事务控制?
    前言公司业务中遇到一个需求,需要同时修改最多约5万条数据,而且还不支持批量或异步修改操作。于是只能写个for循环操作,但操作耗时太长,只能一步一步寻找其他解决方案。具体......
  • 手把手教你如何通过Java给图片添加文字和图片水印
    我报名参加金石计划1期挑战——瓜分10万奖池,这是我的第3篇文章,点击查看活动详情本文首发于个人网站前言最近工作上有个需求,动态生成一张图片,具体来说就是基于模版图片动......
  • 第一百一十六篇: JavaScript理解对象
    好家伙,本篇为《JS高级程序设计》第八章“对象、类与面向对象编程”学习笔记 1.关于对象ECMA-262将对象定义为一组属性的无序集合。严格来说,这意味着对象就是一组没有特......
  • java.lang.ClassNotFoundException: javax.servlet.Filter
    java.lang.ClassNotFoundException:javax.servlet.Filter:有两个原因:(1)在maven中的作用域,不能是provided,需要是compile就是默认的scope(2)spring-boot-starter-tomcat的版本bu......