java 8中使用Optional 避免空指针

您所在的位置:网站首页 optional反义词强制的 java 8中使用Optional 避免空指针

java 8中使用Optional 避免空指针

2023-10-15 10:21| 来源: 网络整理| 查看: 265

Optional 类的引入要求程序员强制处理和避免空指针。Optional 需要使用在可能为null的变量上。很多时候,程序员都是不判空的,这些BUG就像定时炸弹一样,使用Optional 类等于强迫程序员做好判空处理,减少可能的损失。

Optional 类的设计是十分必要的,提前处理可能发生的错误,而不是等到逻辑处理完了再报告错误。 如果返回的null不会终止代码逻辑的运行,比如Java的Map的get方法传了错误类型的key返回null,那么开发者可能会花大量的时间去定位错误的原因,尤其是对于那些庞大的系统来说,无疑是大海捞针。 Scala、lisp、hashshell、erlang等函数式编程语言无一例外地,都对NullPointerException进行了处理,都有Optional的概念,Java8是借鉴了他们。

阿里手册中也提到我们需要使用 Optional 类

【推荐】防止 NPE,是程序员的基本修养,注意 NPE 产生的场景: 1) 返回类型为基本数据类型,return 包装数据类型的对象时,自动拆箱有可能产生 NPE。 反例: public int f() { return Integer 对象}, 如果为 null,自动解箱抛 NPE。 2) 数据库的查询结果可能为 null。 3) 集合里的元素即使 isNotEmpty,取出的数据元素也可能为 null。 4) 远程调用返回对象时,一律要求进行空指针判断,防止 NPE。 5) 对于 Session 中获取的数据,建议进行 NPE 检查,避免空指针。 6) 级联调用 obj.getA().getB().getC();一连串调用,易产生 NPE。 正例:使用 JDK8 的 Optional 类来防止 NPE 问题。

Optional 使用思路

解决办法就是3个步骤: • 包装value:of(x)传入的对象不能为null,而ofNullable(x)是支持传入null的对象,一般用使用Optional.ofNullable()。 • 逐层安全地拆解value:map()。 • 最终返回:orElse()/orElseGet()/orElseThrow()。

image.png

使用示例

Person 类

public class Person { private String name; private Integer age; public void setName(String name) { this.name = name; } public void setAge(Integer age) { this.age = age; } @Override public String toString() { return "Person{" + "name='" + name + '\'' + ", age=" + age + '}'; } }

用法1、使用内部的ofNullable去掉if的写法,将返回值set到其它对象 我比喜欢用这个方法,毕竟不要写if xx!=null 。可以直接在ifPresent() 小括号里写各种逻辑。

public static void main(String[] args) { HashMap objectObjectHashMap = new HashMap(); objectObjectHashMap.put("name", "kawai"); objectObjectHashMap.put("age", 18); Person a = new Person(); //TODO 使用内部的ifPresent避免了空指针异常 Optional.ofNullable(objectObjectHashMap.get("name1")) .ifPresent(name -> a.setName(name.toString().toUpperCase())); Optional.ofNullable(objectObjectHashMap.get("age1")) .ifPresent(age -> a.setAge(Integer.valueOf(age.toString()) + 18)); System.out.println(a); }

用法2、链式调用map()方法。消除多重if xxx!=null; 嵌套 想要实现a、b、c三个变量的加法乘法的组合运算。但是三个变量都有可能为null。所以需要加三层if判断:

Integer a = getA(); if (a != null) { Integer b = getB(); if ( b != null) { Integer c = getC(); if ( c != null) { return (a + b) * c; } else return null; } else return null; } else return null;

改写成这样,是不是简洁很多了?

Optional result = Optional.ofNullable(getA()) .flatMap(a -> Optional.ofNullable(getB()).map( b -> a + b )) .flatMap(sum -> Optional.ofNullable(getC()).map( c -> sum * c ))

用法3、使用orElse,为null则返回默认值

static class UserMapper { public static BizUser selectBizUserByEmployeeNum(String employeeNum) { return null; } } public static Integer run() { BizUser user = UserMapper.selectBizUserByEmployeeNum("3306"); return Optional.ofNullable(user).map(BizUser::getDeptId) .filter(deptId -> deptId.intValue() != 0).orElse(1122); } public static void main(String[] args) { Integer run = run(); System.out.println(run); }

用法4、使用orElseThrow,为null则抛出异常到上层中断执行

static class UserMapper{ public static BizUser selectBizUserByEmployeeNum(String employeeNum) { return null; } } public static void main(String[] args) { BizUser user = UserMapper.selectBizUserByEmployeeNum("3306"); Optional.ofNullable(user).map(BizUser::getDeptId).filter(deptId -> deptId.intValue() != 0) .orElseThrow(() -> new CustomException("查询到的user为空!")); }

用法5、使用filter完成除非空判断以外的其它判断 另外,如果你需要对返回值进行判断,比如结果是否大于某个值等,可以使用Optional的filter方法。

HashMap objectObjectHashMap = new HashMap(); objectObjectHashMap.put("name", "kawai"); objectObjectHashMap.put("age", 19); //TODO 使用内部的ifPresent避免了空指针异常 Object a = Optional.ofNullable(objectObjectHashMap.get("age")) .filter(age -> Integer.parseInt(age.toString()) >= 18).orElse(18); System.out.println(a); 来看看错误用法(滥用Optional)

错误用法1、把if xx!=null 换成了 name1.isPresent() 这种方法和if xx!=null 差不多,还有if条件。不好用!把isPresent()当做判断空指针的方法,又回归以前的if嵌套,毫无意义 。 个人觉得isPresent()不应该暴露出来,放在Optional内部使用更好。

public static void main(String[] args) { HashMap objectObjectHashMap = new HashMap(); objectObjectHashMap.put("name", "小明"); objectObjectHashMap.put("age", 18); Person a = new Person(); Optional name1 = Optional.ofNullable(objectObjectHashMap.get("name1")); if (name1.isPresent()) { a.setName(name1.get().toString().toUpperCase()); } Optional age = Optional.ofNullable(objectObjectHashMap.get("age1")); if (age.isPresent()) { a.setAge(Integer.valueOf(age.get().toString()) + 18); } System.out.println(a); } }

错误用法2、这样还是会出现空指针,因为get()后返回的就是裸露的值

public static void main(String[] args) { HashMap objectObjectHashMap = new HashMap(); objectObjectHashMap.put("name", null); objectObjectHashMap.put("age", 18); Person a = new Person(); //TODO 下面的语句还是会报空指针NoSuchElementException,get()方法返回的是裸露的值。 String name = Optional.ofNullable(objectObjectHashMap.get("name")).get().toString() .toUpperCase(); a.setName(name); Optional.ofNullable(objectObjectHashMap.get("age")) .ifPresent(age -> a.setAge(Integer.valueOf(age.toString()) + 18)); System.out.println(a); }

注意:请不要直接使用get()方法获得值,因为它还是会造成空指针。

错误用法3、被Optional.ofNullable()包装的表达式这样写照样出现空指针

Optional.ofNullable(((Integer) null).toString()).orElse("123");

请使用map()函数把toString()方法单独提出来。

Optional.ofNullable(((Integer) null)).map(m->m.toString()).orElse("123"); orElse和orElseGet有什么区别?

对比下面代码的执行结果: eg1

private static Integer query(){ System.out.println("查询值"); return 1122; } public static void main(String[] args) { HashMap map = new HashMap(1); map.put("age1",1); System.out.println(Optional.ofNullable(map.get("age1")).orElse(query())) ; }

查询值 1122

eg2

private static Integer query(){ System.out.println("查询值"); return 1122; } public static void main(String[] args) { HashMap map = new HashMap(1); map.put("age1",1); System.out.println(Optional.ofNullable(map.get("age1")).orElseGet(()->query())) ; }

1

显然,当ofNullable内部参数非空时,不会去调用默认值里的方法去获取默认值。这也算是一个优化。若query()方法实现是一个网络请求或者数据库操作这样的耗时操作。那么这里请使用orElseGet而不是orElse! 当然若默认值只是一个常量,那么使用orElse语法更简洁。

使用注意

不能使用Optional到java bean的属性中 由于Optional并没有实现Serializable接口,所以不推荐直接作为pojo字段使用。

不推荐将方法返回值设置为Optional 虽然Optional能够防止空指针,有些类里的方法强制把返回值都定为Optional类型。 1、比如java8的stream流api java.util.stream.Stream

Optional max(Comparator


【本文地址】


今日新闻


推荐新闻


CopyRight 2018-2019 办公设备维修网 版权所有 豫ICP备15022753号-3