• Java8-Optional与null


    对null进行处理

    程序中经常需要对null情况进行处理,比如Course类中有一个List stuList属性,Student有一个name属性。
    现在想要查看某个student的name属性的长度,不负责任(不处理null)的写法如下:

    Course course = new Course("数学");
    Student s0 = new Student("s0");
    Student s1 = new Student(null);
    Student s2 = null;
    course.addStudent(s0, s1, s2);
    int index = 1;
    String name = course.getStuList().get(index).getName();  //index = 1或2都会抛出NullPointerException
    System.out.println(name.length());
    

    String name = course.getStuList().get(index).getName()这样写固然省事,但是当s2为null或者s1对象的name为null时,都会抛出大家熟悉的NullPointerException
    所以需要使用大量的if...else语句对变量否为null进行判断。

    int index = 1;
    Student student = course.getStuList().get(index);
    String errorMessage = "处理过程中无null";
    String name = "";
    if(student!=null) {
    	name = student.getName();
    	if(name!=null) {
    		System.out.println(name.length());
    	}else {
    		errorMessage="student对象的name为null";
    		
    	}
    }else {
    	errorMessage="student对象为null";
    }
    sysout
    System.out.println(errorMessage);
    

    使用Optional处理null

    null是一个特殊值,并不是一个类型,student.getName()可能返回null也可能返回String类型对象,这是两种不同的情况。
    Java 8中引入了Optional类型来统一处理null与对应的类型。该类型为容器类型,可以包含某个类型的非空值与空值,所以可以对他们统一处理。
    Optional基本用法如下:

    Optional<Student> stuNullable = Optional.empty();//代表null的Optional对象
    Student stu = new Student("s0");
    System.out.println(stuNullable.isPresent());//false,判断里面是否存在Student对象
    //Student student = stuNullable.get();//将抛出NoSuchElementException
    Optional<Student> stuNotNull = Optional.ofNullable(stu);  //代表非空的Student,实际上将stu对象放入该Optional容器中
    System.out.println(stuNotNull.isPresent());//true
    Student student = stuNotNull.get(); //返回刚才装入的stu对象
    

    其中 Optional.ofNullable(stu)的作用是:当stu为null时,返回Optional.empty(),否则返回包含stu对象的Optional对象(即,Optional.of(stu))。
    到现在为止我们可以使用Optional来代替前面的对null值的直接处理,代码如下:

    Course course = new Course("数学");
    Student s0 = new Student("s0");
    Student s1 = new Student(null);
    Student s2 = null;
    course.addStudent(s0, s1, s2);
    int index = 1;
    Student student = course.getStuList().get(index);
    Optional<Student> stu = Optional.ofNullable(student);//将student放入Optioanl中进行处理
    String errorMessage = "处理过程中无null";
    String name = "";
    if (stu.isPresent()) {
    	Optional<String> nameOfNullable = Optional.ofNullable(stu.get().getName());
    	if (nameOfNullable.isPresent()) {
    		name = nameOfNullable.get();
    		System.out.println(name.length());
    	}else {
    		errorMessage="student对象的name为null";
    	}
    }else {
    	errorMessage="student对象为null";
    }
    System.out.println(errorMessage);
    

    呃.....代码更复杂了。但至少让你不能忽略null值了。
    再看看Optional中的其他方法ifPresentorElse

    stu.ifPresent(e->System.out.println(e.getName()));//如果存在对象,则Optional中的包含的对象getName返回的值
    Student defaultStudent = new Student("默认对象");
    Student orElse = stu.orElse(defaultStudent); //如果存在对象直接返回该对象,否则返回defaultStudent对象
    

    似乎无补于事。

    Optional的map方法

    对上面的代码可以使用map方法进行改造,如下所示:

    int index = 0;
    String errorMessage = "处理过程有null";
    String name = "";
    Optional<Course> courseNullable = Optional.ofNullable(course);
    Optional<String> resultOptional = courseNullable.map(e->e.getStu(index)).map(e->e.getName());
    String result = resultOptional.orElse(errorMessage);//resultOptional中可能为null,也可能有值。如果未null,则返回errorMessage。
    System.out.println(result);
    

    map方法入参为Function类型(可以将Optional中原来的类型转换成新的类型)。
    map(e->e.getStu(index))就是将Courset类型转换成Student类型
    map(e->e.getName())则是将Student类型转换成String类型。
    map方法返回值为Optional类型,所以可以以链式风格map(e->e.getStu(index)).map(e->e.getName())取代上面复杂的if...else处理。

    这个例子体现出了Optional的优越性,即可以通过其优雅的处理null值。
    窃以为,相比较Optioanl带来的优越性,其语法还是有点复杂。建议先掌握其语法,这样至少看别人的相关代码的时候不会无所适从。

    基础代码

    
    class Student {
    	private String name;
    
    	public String getName() {
    		return name;
    	}
    
    	public void setName(String name) {
    		this.name = name;
    	}
    
    	public Student(String name) {
    		this.name = name;
    	}
    
            @Override
            public String toString() {
    	    return "Student [name=" + name + "]";
            }
    
    }
    
    class Course {// 课程
    	private String name;
    	private List<Student> stuList;
    
    	public String getName() {
    		return name;
    	}
    
    	public void setName(String name) {
    		this.name = name;
    	}
    
    	public void addStudent(Student... stus) {
    		for (Student student : stus) {
    			stuList.add(student);
    		}
    	}
    
    	public Student getStu(int i) {
    		return stuList.get(i);
    	}
    
    	public List<Student> getStuList() {
    		return stuList;
    	}
    
    	public Course(String name) {
    		this.name = name;
    		stuList = new ArrayList<>();
    	}
    
    }
    

    使用Optional改造Course

    查看jdk文档,Optional的说明如下:

    Optional is primarily intended for use as a method return type where there is a clear need to represent "no result," and where using null is likely to cause errors.

    即,Optional主要用于修饰方法的返回类型,来表示“没有结果”这个返回结果,还用在使用null作为返回类型容易导致出错的地方。
    那么,我么可以使用Optional来改造Course的Student getStu(int i)代码:

    	public Student getStu(int i) {
    		return stuList.get(i);
    	}
    

    该段代码主要有两个问题:

    1. i可能越界。
    2. get(i)返回的值可能为null。

    改造后代码如下:

    public Optional<Student> getStu(int i) {
    	if (i >= stuList.size())
    		return Optional.empty(); // 带代表null的Optional实例
    	Student student = stuList.get(i);
    	if (student == null)
    		return Optional.empty();
    	return Optional.of(student);
    }
    

    或者进一步简化,改造成这样:

    public Optional<Student> getStu(int i) {
    	if (i >= stuList.size())
    		return Optional.empty(); 
    	Student student = stuList.get(i);
    	return Optional.ofNullable(student);
    }
    

    Optional.ofNullable方法可以处理入参为null的情况。其相关文档描述如下

    Returns an Optional describing the specified value, if non-null, otherwise returns an empty Optional.

    定义测试方法如下

    private static void getStuByIndex(Course course, int index) {
    	Student stu0 = course.getStu(index).orElse(new Student("默认学生"));//如果为null,则返回“默认学生”对象
    	System.out.println(stu0);
    }
    

    测试代码如下:

    Course course = new Course("数学");
    Student s0 = new Student("s0");
    Student s1 = new Student(null);
    Student s2 = null;
    course.addStudent(s0, s1, s2);
    getStuByIndex(course, 0);
    getStuByIndex(course, 1);
    getStuByIndex(course, 2);//s2为null
    getStuByIndex(course, 3);//越界,返回Optional.empty()
    

    输出:

    Student [name=s0]
    Student [name=null]
    Student [name=默认学生]
    Student [name=默认学生]
    

    测试flatmap代码如下

    Optional<Course> courseNullable = Optional.ofNullable(course);
    Optional<String> r0 = courseNullable.flatMap(e->e.getStu(0)).map(Student::getName);
    Optional<String> r1 = courseNullable.flatMap(e->e.getStu(1)).map(Student::getName);
    Optional<String> r2 = courseNullable.flatMap(e->e.getStu(2)).map(Student::getName);
    Optional<String> r3 = courseNullable.flatMap(e->e.getStu(3)).map(Student::getName);
    System.out.println(r0);
    System.out.println(r1);//Student的name属性为null,返回Optional.empty()
    System.out.println(r2);//Student为null,返回Optional.empty()
    System.out.println(r3);//数组越界,返回Optional.empty()
    

    输出如下:

    Optional[s0]
    Optional.empty
    Optional.empty
    Optional.empty
    

    对比以前的代码

    Optional<String> resultOptional = courseNullable.map(e->e.getStu(index)).map(e->e.getName());
    

    map(e->e.getStu(0))改成了flatMap(e->e.getStu(0))。这是因为改造前的e->e.getStu(0)返回的是Student对象,
    而改造后返回的是Optional<Student>类型对象。观察改造后的代码段:

    Optional<Optional<Student>> xa = courseNullable.map(e->e.getStu(2));
    Optional<Student> xb = courseNullable.flatMap(e->e.getStu(2));
    

    可以看到xa并不是我们想要的结果,而xb才是我们想要的结果。前面已经提到Optional相当于一个容器,那么Optional<Optional<Student>>
    相当于容器中嵌套一个容器,在这里我们需要关注的是大容器里面的Optional<Student>类型对象。flatMap中的flat可以理解为平坦化,相当于
    从嵌套的容器中取出自己真正想要的元素。

  • 相关阅读:
    POJ2104&&HDU2665(静态区间第K小)
    HDU4763
    js 获取视频的第一帧
    hadoop 集群配置
    redis_cli 批量删除
    vmware centos 7 更新vmware-tools
    php计算两个整数的最大公约数常用算法小结
    centOS 7 配置NAT模式
    centOS配置NAT模式
    show table status 获取表的信息
  • 原文地址:https://www.cnblogs.com/zhrb/p/10040095.html
Copyright © 2020-2023  润新知