1. 文章引言
今天,测试人员发现一个java.lang.NullPointerException
异常,即空指针异常。
于是,通过接口排查,发现此前开发人员,使用java8 lamda
表达式写的。
写的很漂亮,但没有做严谨的判断。
为了复现这个错误,我需要写个用来测试的Student
类,如下代码所示:
@Data
@AllArgsConstructor
@NoArgsConstructor
public class Student {
private String name;
private String sex;
private int age;
public static Student getInstance() {
Student student = new Student();
student.setName("念兮为美");
student.setAge(16);
student.setSex("男");
return student;
}
public static void main(String[] args) {
}
}
我们在main方法中写如下代码:
public static void main(String[] args) {
Student student = getInstance();
Student student1 = getInstance();
student.setName("张三");
Student student2 = getInstance();
List<Student> students = new ArrayList<>();
students.add(student);
students.add(student1);
students.add(student2);
students.add(null);
// 只输出姓名
Optional.ofNullable(students).orElse(new ArrayList<>()).stream()
.map(Student::getName)
.forEach(t -> System.out.println(t));
}
运行main
方法,即报出如下错误:
因为students
集合在添加第4
个对象时,没有做出对象为空的判断。
所以,输出前3
个姓名,第4
个即报空指针异常。
2. Optional.ofNullable
Optional.ofNullable
是判断对象不能为空,如果对象为容器的话,则无法判断容器中的对象是否为空,如下代码所示:
public static void main(String[] args) {
Student student = getInstance();
Student student1 = getInstance();
student.setName("张三");
Student student2 = getInstance();
List<Student> students = new ArrayList<>();
students.add(student);
students.add(student1);
students.add(student2);
students.add(null);
// 判断students集合是否为空,如果不为空,则执行,但无法判断集合中对象是否为空
Optional.ofNullable(students).ifPresent(t -> System.out.println(t));
}
输出结果:
2.1 ifPresent
ifPresent
表示如果存在值,则使用该值调用指定的使用者,否则不执行任何操作。
该值表示Optional.ofNullable(T value)中的value对象,比如本示例中的students
对象,如下为ifPresent
的源码:
/**
* If a value is present, invoke the specified consumer with the value,
* otherwise do nothing.
*
* @param consumer block to be executed if a value is present
* @throws NullPointerException if value is present and {@code consumer} is
* null
*/
public void ifPresent(Consumer<? super T> consumer) {
if (value != null)
consumer.accept(value);
}
2.2 orElse
orElse
表示如果Optional.ofNullable
中的对象为空,则返回一个新的对象。
public static void main(String[] args) {
// 断students集合是否为空,如果为空,orElse创建新的集合返回
Student student3=new Student();
student3.setName("新学生");
//定义空集合
List<Student> students1 = null;
//创建orElse后的集合
ArrayList<Student> objects = new ArrayList<>();
objects.add(student3);
Optional.ofNullable(students1).orElse(objects).forEach(t -> System.out.println(t));
}
输出结果:
orElseGet
用的是Supplier
接口返回的对象。
supplier
接口就一个get
方法,无入参,出参要和Optional
的对象同类型。
orElseThrow
用的是Supplier
接口返回的对象,这个对象必须要实现Throwable
。
supplier
接口就一个get
方法,无入参,出参要实现Throwable
。
3. forEach
相当于for
,遍历集合。
上面的代码也演示过,如下代码再次演示:
System.out.println("forEach 遍历结果:");
students.forEach(t -> System.out.println("\t\t" + t));
System.out.println("for 遍历结果:");
for (Student student3 : students) {
System.out.println("\t\t" +student3);
}
4. filter
filter
用来过滤数据的,使用如下代码可以解决文章引言中的错误:
// 只输出姓名
Optional.ofNullable(students).orElse(new ArrayList<>()).stream()
.filter(Objects::nonNull)
.map(Student::getName)
.forEach(t -> System.out.println(t));
输出结果:
5. map
map
表示后面的值替换前面的值,我们一般可以只获取对象中的属性值。
Optional.ofNullable(students).orElse(new ArrayList<>()).stream()
.filter(Objects::nonNull)
.map(Student::getName)
.forEach(t -> System.out.println(t));
输出结果:
6. collect
实现各种有用的减少操作,如将元素累积到集合中,汇总根据各种标准的元素,等等。
String collect =
Optional.ofNullable(students).orElse(new ArrayList<>()).stream()
.filter(Objects::nonNull)
.map(Student::getName)
.collect(Collectors.joining(","));
System.out.println(collect);
输出结果:
collect
也有如下如下用法:
Map<String, Integer> collect1 =
Optional.ofNullable(students).orElse(new ArrayList<>()).stream()
.filter(Objects::nonNull)
.collect(Collectors.toMap(Student::getName, Student::getAge));
collect1.forEach((name, age) -> System.out.printf("name -> %s, age -> %d\n", name, age));
但是报错了,为什么这样呢?因为map
的key
不能重复。
我们上述代码:name
作为key
值,但出现了两个念兮为美,即key
值重复了。
可以使用如下代码解决:
Map<String, Integer> collect1 =
Optional.ofNullable(students).orElse(new ArrayList<>()).stream()
.filter(Objects::nonNull)
.collect(
Collectors.toMap(Student::getName, Student::getAge, (value1, value2) -> value2));
collect1.forEach((name, age) -> System.out.printf("name -> %s, age -> %d\n", name, age));
7. distinct
distinct
表示去重。
在之前的代码中,念兮为美每次都输出两次,我们可以使用distinct
去重,如下代码所示:
String collect1 =
Optional.ofNullable(students).orElse(new ArrayList<>()).stream()
.filter(Objects::nonNull)
.map(Student::getName)
.distinct()
.collect(Collectors.joining(","));
System.out.println(collect1);
输出结果:
8. 总结
java lamda
表达式有很多知识点,需要自己去实践。