java.util.Optional基础

传统处理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(); } } }

__EOF__

本文作者Lian_tiam
本文链接https://www.cnblogs.com/ancientlian/p/16995496.html
关于博主:评论和私信会在第一时间回复。或者直接私信我。
版权声明:本博客所有文章除特别声明外,均采用 BY-NC-SA 许可协议。转载请注明出处!
声援博主:如果您觉得文章对您有帮助,可以点击文章右下角推荐一下。您的鼓励是博主的最大动力!
posted @   Lian_tiam  阅读(252)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 25岁的心里话
· 闲置电脑爆改个人服务器(超详细) #公网映射 #Vmware虚拟网络编辑器
· 基于 Docker 搭建 FRP 内网穿透开源项目(很简单哒)
· 零经验选手,Compose 一天开发一款小游戏!
· 一起来玩mcp_server_sqlite,让AI帮你做增删改查!!
点击右上角即可分享
微信分享提示