Optional类是 Java 8 引入的一个很有趣的特性。它主要解决的问题是臭名昭著的空指针异常(NullPointerException)
Optional 是一个对象容器,具有以下两个特点:
举一个简单的例子,在 Java 8 之前,任何访问对象方法或属性的调用都可能导致 NullPointerException:
用户 -> 家庭住址 -> 城市 ->邮编
String postCode = user.getAddress().getCity().getPostCode();
在这个示例中,为了避免异常,就得在访问每一个值之前对其进行明确地检查:
if (user != null) {
Address address = user.getAddress();
if (address != null) {
City city= address.getCity();
if (city != null) {
String postCode = city.getPostCode();
if (postCode != null) {
//对postCode进行操作
test(postCode);
}
}
}
}
这很容易就变得冗长,难以维护。
为了简化这个过程,我们就可以用 Optional 类。
Optional类的实例创建有三种方式:
isPresent():判断optional是否为空,如果空则返回false,否则返回true
ifPresent(Consumer c):如果optional不为空,则将optional中的对象传给Comsumer函数
public class OptionalDemo {
public static void main(String[] args) {
User user = new User("王也", "5");
User userNull= null;
Optional<User> optional = Optional.ofNullable(user);
System.out.println(optional.isPresent());
optional.ifPresent(u -> System.out.println("optional不为null "+u));
}
}
User user = new User("王也", "5");
User userNull= null;
//orElse的工作方式非常直接,如果有值则返回该值,否则返回传递给它的参数值:
User user1 = Optional.ofNullable(userNull).orElse(user);
System.out.println(user1);
//控制台输出:User(name=王也, age=5)
User user1 = Optional.ofNullable(userNull).orElseGet(()->user );
//结果同上
//这个方法让我们有更丰富的语义,可以决定抛出什么样的异常,而不总是抛出 NullPointerException。
User result = Optional.ofNullable(userNull)
.orElseThrow( () -> new IllegalArgumentException());
乍一看,这两种方法似乎起着同样的作用。然而事实并非如此。我们创建一些示例来突出二者行为上的异同。
(1)当对象为空时:
public class OptionalDemo {
public static void main(String[] args) {
User userNull = null;
System.out.println("使用orElse():");
User result = Optional.ofNullable(userNull).orElse(createNewUser());
System.out.println("使用orElseGet():");
User result2 = Optional.ofNullable(userNull).orElseGet(() -> createNewUser());
}
private static User createNewUser() {
System.out.println("Creating New User");
return new User("新的user对象", "1234");
}
}
上面的代码中,两种方法都调用了 createNewUser() 方法,这个方法会记录一个消息并返回 User 对象。
控制台输出:
使用orElse():
Creating New User
使用orElseGet():
Creating New User
由此可见,当对象为空而返回默认对象时,行为并无差异。
(2)当对象不为空时:
public class OptionalDemo {
public static void main(String[] args) {
User user = new User("王也", "5");
System.out.println("使用orElse():");
User result = Optional.ofNullable(user).orElse(createNewUser());
System.out.println("使用orElseGet():");
User result2 = Optional.ofNullable(user).orElseGet(() -> createNewUser());
}
private static User createNewUser() {
System.out.println("Creating New User");
return new User("新的user对象", "1234");
}
}
控制台输出:
使用orElse():
Creating New User
使用orElseGet():
这个示例中,两个 Optional 对象都包含非空值,两个方法都会返回对应的非空值。不过,orElse() 方法仍然创建了 User 对象。与之相反,orElseGet() 方法不创建 User 对象。
在执行较密集的调用时,比如调用 Web 服务或数据查询,这个差异会对性能产生重大影响。
filter(Predicate p):filter() 接受一个 Predicate 参数,返回测试结果为 true 的值。如果测试结果为 false,会返回一个空的 Optional。
map(Function
flatMap(Function
map() 对值应用(调用)作为参数的函数,然后将返回的值包装在 Optional 中。这就使对返回值进行链试调用的操作成为可能 —— 这里的下一环就是 orElse()。
flatMap() 也需要函数作为参数,并对值调用这个函数,然后直接返回结果。
4和5示例:
public class OptionalDemo {
public static void main(String[] args) {
List<Student> studentList = initData();
for (Student student : studentList) {
Optional<Student> studentOptional = Optional.of(student);
Integer score = studentOptional.filter(s -> s.getAge() >= 18).map(Student::getScore).orElse(0);
if (score > 80) {
System.out.println("入选:" + student.getName());
}
}
}
private static List<Student> initData(){
Student s1 = new Student("张三", 19, 80);
Student s2 = new Student("李四", 19, 50);
Student s3 = new Student("王五", 23, null);
Student s4 = new Student("赵六", 16, 90);
Student s5 = new Student("钱七", 18, 99);
Student s6 = new Student("孙八", 20, 40);
Student s7 = new Student("吴九", 21, 88);
return Arrays.asList(s1, s2, s3, s4, s5, s6, s7);
}
}
使用stream流的写法:
public static void main(String[] args) {
List<Student> studentList = initData();
List<String> studentName = studentList.stream().filter(student -> student.getAge() >= 18)
.filter(student -> student.getScore() != null && student.getScore() > 80)
.map(student -> student.getName()).collect(Collectors.toList());
System.out.println(studentName);
}
控制台输出:
入选:钱七
入选:吴九