JDK8新特性-Optional类

举报
摸鱼打酱油 发表于 2022/04/01 22:40:03 2022/04/01
【摘要】 个人简介作者是一个来自河源的大三在校生,以下笔记都是作者自学之路的一些浅薄经验,如有错误请指正,将来会不断的完善笔记,帮助更多的Java爱好者入门。@[toc] JDK8新特性-Optional类 教程概述本教程附有非常多的例子,看完肯定能懂Optional类!看完本教程,对于Optional类的api基本的使用完全没有问题!本教程借鉴过很多其他大佬的教程,并进行总结创新,难免会有相同之处...

个人简介

作者是一个来自河源的大三在校生,以下笔记都是作者自学之路的一些浅薄经验,如有错误请指正,将来会不断的完善笔记,帮助更多的Java爱好者入门。

@[toc]

JDK8新特性-Optional类

教程概述

  • 本教程附有非常多的例子,看完肯定能懂Optional类!
  • 看完本教程,对于Optional类的api基本的使用完全没有问题!
  • 本教程借鉴过很多其他大佬的教程,并进行总结创新,难免会有相同之处。
  • 本教程会涉及一些源码。

Optional的简单介绍

  • 每个程序员都会遇到的Exception其中最常见的莫过于NullPointerException异常,为了避免这个异常的发生我们常用if去判断对象是否为null再进行使用,这样就显得很乏味,于是Optional出现了。
  • Optional的好处是:使用链式编程的方式更优雅的解决NullPointerException问题

简单对比两种防止空指针方式

准备环境

	  //准备环境
      User user = new User();
      user.setUsername("aaa");
      UserDetail userDetail = new UserDetail();
      userDetail.setIcon("icon");
      userDetail.setSignature("ccccd");
      user.setUserdetail(userDetail);
      UserAuthority userAuthority = new UserAuthority();
      userAuthority.setAuthority(2);
      userDetail.setUserAuthority(userAuthority);
class User{

    private String username; //用户名
    private UserDetail userdetail; //用户细节

    //省略getter/setter/tostring
}
class UserDetail{

    private String icon;//头像
    private String signature;//个性前面
    private UserAuthority userAuthority; //用户权限

    //省略getter/setter/tostring
}
class UserAuthority{

    private int authority; //权限

    //省略getter/setter/tostring
}

JDK1.8之前的普通方式

      //JDK1.8之前,如果我们要查询用户权限。要这样子
      if(user!=null){

          if(user.getUserdetail()!=null){

              if(user.getUserdetail().getUserAuthority()!=null)
              {
                  System.out.println("JDK1.8之前查权限:"+user.getUserdetail().getUserAuthority().getAuthority());
              }else {
                  System.out.println(333);
              }

          }
      }

JDK1.8之后使用Optional方式

	  //JDK1.8之后,如果我们要查询用户权限。可以使用Optional类
      Integer integer = Optional.ofNullable(user)
              //还没有经过map,当前stream是:user对象

              //经过了第一次map变成userdetail对象
              .map(u -> u.getUserdetail())
              //经过了第二次userauthority
              .map(ud -> ud.getUserAuthority())
              //authority
              .map(a -> a.getAuthority())
              //如果哪一层map出现null了,则会直接跳到下面这个orElse去执行。
              .orElse(333);
      System.out.println(integer);

创建Optional实例的三种方式

  • 由于Optional类的构造方法全部都被private修饰,所以不能通过new关键字去创建,必须要通过以下三种方法进行创建。
	private Optional() {
        this.value = null;
    }
	private Optional(T value) {
        this.value = Objects.requireNonNull(value);
    }
  • Optional类被final进行修饰,故无法对它进行继承。
public final class Optional<T> {
    Optional类核心代码
}

of方法

of方法的特点:

  • 不允许Optional的value为null否则会立刻报错NullPointerException异常
of方法创建Optional实例
Optional<String> stringOptional = Optional.of("hello");
为什么of方法的值不能为null,否则就会报错?源码分析
    public static <T> Optional<T> of(T value) {
        return new Optional(value);
    }
	private Optional(T value) {
        this.value = Objects.requireNonNull(value);
    }

Objects.requireNonNull源码

	public static <T> T requireNonNull(T obj) {
        if (obj == null) {
            throw new NullPointerException();
        } else {
            return obj;
        }
    }

ofNullable方法

  • ofNullable的value可以为null,并且不会报错
ofNullable方法创建Optional实例
Optional<Object> optional = Optional.ofNullable("hello");
为什么ofNullable方法的值可以为null?源码分析
	public static <T> Optional<T> ofNullable(T value) {
        return value == null ? empty() : of(value);
    }
  • 可以看出当value==null时,此时Optional的对象由empty方法创建,而empty方法的返回的Optional对象是由Optional类内部实例化的一个Optional实例,名字为EMPTY,并且是被private static final修饰。
	private static final Optional<?> EMPTY = new Optional();	
	public static <T> Optional<T> empty() {
        Optional<T> t = EMPTY;
        return t;
    }
  • 当value!=null时,则调用of方法进行创建,虽然of方法不能为null否则会报错,但是此时的value一定不为空,所以of方法一定不会报错。故这个ofNullable方法一定不会报错。

empty方法

  • empty方法的返回的Optional对象是由Optional类内部实例化的一个Optional实例,名字为EMPTY,并且是被private static final修饰
empty方法源码分析
	private static final Optional<?> EMPTY = new Optional();	
	public static <T> Optional<T> empty() {
        Optional<T> t = EMPTY;
        return t;
    }

isPresent方法

  • 判断Optional的value是否存在,也就是是否不为null

isPresent方法的使用

	//准备环境
    User user = new User();
    user.setUsername("aaa");
    UserDetail userDetail = new UserDetail();
    userDetail.setIcon("icon");
    userDetail.setSignature("ccccd");
    user.setUserdetail(userDetail);
    UserAuthority userAuthority = new UserAuthority();
    userAuthority.setAuthority(2);
    userDetail.setUserAuthority(userAuthority);

    Optional<User> optionalUser = Optional.ofNullable(user);

    boolean present = optionalUser.isPresent();

    System.out.println("Optional的value是否存在?"+present);

    if(present){
      System.out.println(optionalUser.get()); //此时一定不为null,present为true代表存在
    }

isPresent源码分析

	private final T value;	
	public boolean isPresent() {
        return this.value != null;
    }
  • Optional一旦创建,就会往value放值,isPresent方法的作用就是判断value是否不为空,也就是是否存在

  • 还有就是String s="",也代表存在,而String s=null,则是不存在。

ifPresent方法

  • 一定要区别于isPresent和ifPresent方法。

ifPresent方法使用

	Optional<String> optionalS = Optional.ofNullable(null);

      //如果Optional的value存在则会执行System.out.println("ifpresent="+s)这段代码,反之则不会执行。
      optionalS.ifPresent(s->System.out.println("ifpresent="+s));

ifPresent源码分析与isPresent区别

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

    }
  • ifPresent没有返回值,接收参数是lambda表达式,如果this.value不会空才会执行这个lambda表达式。
	public boolean isPresent() {
        return this.value != null;
    }
  • 而isPresent方法是不用参数的,并且返回boolean值,也是判断this.value是否存在。本质上都差不多

orElse方法

  • 当optional的value为null,或者经过map映射的optional的value为null,则会执行orElse方法
  • 本质上orElse方法就是当optional的value为null时的兜底方法。
	  Optional<String> optional1 = Optional.ofNullable("hello");
      String s1 = optional1.orElse("orElse111---");

      System.out.println(s1); //hello


      Optional<String> optional2 = Optional.ofNullable(null);
      String s2 = optional2.orElse("orElse222---");

      System.out.println(s2); // orElse222---

orElse方法源码解析

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

意思是如果this.value如果存在,返回结果就是这个value,如果value不存在,则会返回other,other就是我们的兜底方案。

orElseGet方法

  • orElse和orElseGet差不多,只是形参不同,orElseGet需要传入lambda的Supplier 表达式,格式为:()->xxx

orElseGet方法使用

	 Object o = Optional.ofNullable(null)
              .orElseGet(() -> "orElseGet");
      System.out.println(o);

orElseGet源码解析

	public T orElseGet(Supplier<? extends T> supplier) {
        return this.value != null ? this.value : supplier.get();
    }

如果this.value存在,则返回this.value,反之则使用传过来的supplier的值

orElse和orElseGet区别

  • 如果optional的value!=null,orElse方法也要执行。即使value不为null,也要执行。
  • 而orElseGet方法只有value==null才会执行
  • 对于orElse方法这种特性是极其浪费性能的。
  private static String getName() {
      System.out.println("getname");
    return "张三";
  }	  
	  String s1 = Optional.ofNullable("666")
              .orElse(getName());
      System.out.println(s1);

输出

getname
666
  private static String getName() {
      System.out.println("getname");
    return "张三";
  }	  
	  String s2 = Optional.ofNullable("777")
              .orElseGet(() -> getName());
      System.out.println(s2);

输出

777

orElseThrow方法

  • 其实就是当value为null时,兜底方法就是抛自定义异常。
	  String n=null;
      String s = Optional.ofNullable(n)
              .orElseThrow(() -> new RuntimeException("excp"));

      System.out.println(s);

输出

Exception in thread "main" java.lang.RuntimeException: excp
	at com.jdk8.optional.Optional06.lambda$main$0(Optional06.java:11)
	at java.base/java.util.Optional.orElseThrow(Optional.java:408)
	at com.jdk8.optional.Optional06.main(Optional06.java:11)

filter方法

filter方法的使用请参照我的一篇Stream流的教程。。。

  • 其实filter过滤就像是if语句,只把符合条件的筛选出来。

使用

	  //准备环境
      User user1 = new User();
      user1.setUsername("aaa");
      User user2 = new User();
      user2.setUsername("bbb");
      User user3 = new User();
      user3.setUsername("ccc");

      Optional<User> optionalUser1 = Optional.ofNullable(user1)
              .filter(x -> x.getUsername().equals("bbb"));

      System.out.println(optionalUser1);//Optional.empty

      Optional<User> optionalUser2 = Optional.ofNullable(user2)
              .filter(x -> x.getUsername().equals("bbb"));//Optional[User{username='bbb', userdetail=null}]

      System.out.println(optionalUser2);

输出

Optional.empty
Optional[User{username='bbb', userdetail=null}]

map方法

map方法的使用请参照我的一篇Stream流的教程。。。

  • map的作用就是把一个对象进行转换,比如下面的,map的作用就是把User对象转换成UserDetail对象。

案例:传入User对象,返回UserDetail对象

	  //准备环境
      User user = new User();
      user.setUsername("aaa");
      UserDetail userDetail = new UserDetail();
      userDetail.setIcon("icon");
      userDetail.setSignature("ccccd");
      user.setUserdetail(userDetail);
      UserAuthority userAuthority = new UserAuthority();
      userAuthority.setAuthority(2);
      userDetail.setUserAuthority(userAuthority);


      //传入User对象,返回UserDetail对象
      Optional<UserDetail> optionalUserDetail =
              Optional
                      .of(user)
                      .map(User::getUserdetail);
      System.out.println(optionalUserDetail.get());

输出

UserDetail{icon='icon', signature='ccccd', userAuthority=UserAuthority{authority=2}}
【版权声明】本文为华为云社区用户原创内容,转载时必须标注文章的来源(华为云社区)、文章链接、文章作者等基本信息, 否则作者和本社区有权追究责任。如果您发现本社区中有涉嫌抄袭的内容,欢迎发送邮件进行举报,并提供相关证据,一经查实,本社区将立刻删除涉嫌侵权内容,举报邮箱: cloudbbs@huaweicloud.com
  • 点赞
  • 收藏
  • 关注作者

评论(0

0/1000
抱歉,系统识别当前为高风险访问,暂不支持该操作

全部回复

上滑加载中

设置昵称

在此一键设置昵称,即可参与社区互动!

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。