- 到目前为止,臭名昭著的空指针异常是导致Java应用程序失败的最常见的原因。以前,为了解决空指针异常,Google公司著名的Guava项目引入了Optional类,Guava通过使用检查空值的方式来防止代码污染,它鼓励程序员写更干净的代码。受到Google Guava的启发,Optional类已经成为Java 8类库的一部分。
- Optional 类(java.util.Optional)是一个容器类,它可以保存类型T的值,代表这个值存在。或者仅仅保存null,表示这个值不存在。原来用null表示一个值不存在,现在Optional可以更好的表达这个概念。并且可以避免空指针异常。
- Optional类的Javadoc描述如下:这是一个可以为null的容器对象。如果值存在,则isPresent()方法会返回true,调用get()方法会返回该对象。
Optional提供很多有用的方法,这样我们就不用显式进行空值检测
- 创建Optional类对象的方法
- Optional.of(T t):创建一个Optional实例,t必须非空
- Optional.empty():创建一个空的Optional实例
- Optional.ofNullable(T t):创建一个Optional实例,t可以为null
- 判断Optional容器中是否包含对象
- boolean isPresent():判断是否包含对象
- void ifPresent(Consumer super T> consumer):如果有值,就执行Consumer接口的实现方法,并且该值会作为参数传给它
- 获取Optional容器的对象
- T get():如果调用对象包含值,返回该值,否则抛异常
- T orElse(T other):如果有值则将其返回,否则返回指定的other对象
- T orElseGet(Supplier extends T> other):如果有值则将其返回,否则返回由Supplier接口实现提供的对象
- T orElseThrow(Supplier extends X> exceptionSupplier):如果有值则将其返回,否则抛出由Supplier接口实现提供的异常
- 对容器中的对象进行操作,以下三个方法和下一篇
Stream流
的方法使用基本一样
- Optional filter(Predicate super T> predicate):如果该值存在并且满足提供的谓词,就返回包含该值的Optional对象;否则返回一个空的Optional对象
- Optional map(Function super T, ? extends U> mapper):如果值存在,就对该值执行提供的mapping函数调用
- Optional flatMap(Function super T, Optional> mapper):如果值存在,就对执行提供的mapping函数调用,返回一个Optional类型的值,否则就返回一个空的Optional对象
public static void main(String[] args){
String s = new String("123");
Optional<String> s1 = Optional.of(s);
System.out.println(s1); // Optional[123]
s = null;
Optional<String> s2 = Optional.of(s);
System.out.println(s2); // java.lang.NullPointerException
}
public static void main(String[] args){
Optional<Object> empty = Optional.empty();
System.out.println(empty); // Optional.empty
}
public static void main(String[] args){
String s1 = new String("123");
Optional<String> o1 = Optional.ofNullable(s1);
System.out.println(o1); // Optional[123]
s1 = null;
Optional<String> o2 = Optional.ofNullable(s1);
System.out.println(o2); // Optional.empty
}
public static void main(String[] args){
String s1 = new String("123");
Optional<String> o1 = Optional.ofNullable(s1);
boolean p1 = o1.isPresent();
System.out.println(p1); // true
s1 = null;
Optional<String> o2 = Optional.ofNullable(s1);
boolean p2 = o2.isPresent();
System.out.println(p2); // false
}
public static void main(String[] args){
String s1 = new String("123");
Optional<String> o1 = Optional.ofNullable(s1);
o1.ifPresent(System.out::println); // 123
s1 = null;
Optional<String> o2 = Optional.ofNullable(s1);
o2.ifPresent(System.out::println); // ...null值,不执行里面函数,无结果
}
public static void main(String[] args){
String s1 = new String("123");
Optional<String> o1 = Optional.ofNullable(s1);
String g1 = o1.get();
System.out.println(g1); // 123
s1 = null;
Optional<String> o2 = Optional.ofNullable(s1);
String g2 = o2.get();
System.out.println(g2); // java.util.NoSuchElementException: No value present
}
public static void main(String[] args){
String s1 = new String("123");
Optional<String> o1 = Optional.ofNullable(s1);
// o1包含对象,不使用234
String or1 = o1.orElse("234");
System.out.println(or1); // 123
s1 = null;
Optional<String> o2 = Optional.ofNullable(s1);
// o2不包含对象,使用345
String or2 = o2.orElse("345");
System.out.println(or2); // 345
}
public static void main(String[] args){
String s1 = new String("123");
Optional<String> o1 = Optional.ofNullable(s1);
// o1包含对象,不执行里面函数式接口,返回123
String or1 = o1.orElseGet(() -> "234");
System.out.println(or1); // 123
s1 = null;
Optional<String> o2 = Optional.ofNullable(s1);
// o2不包含对象,执行里面函数式接口,返回345
String or2 = o2.orElseGet(() -> "345");
System.out.println(or2); // 345
}
public static void main(String[] args){
String s1 = new String("123");
Optional<String> o1 = Optional.ofNullable(s1);
String or1 = o1.orElseThrow(() -> new RuntimeException("123"));
System.out.println(or1); // 123
s1 = null;
Optional<String> o2 = Optional.ofNullable(s1);
// 本行直接异常:Exception in thread "main" java.lang.RuntimeException: 234
o2.orElseThrow(() -> new RuntimeException("234"));
}
public static void main(String[] args){
String s1 = new String("123");
Optional<String> o1 = Optional.ofNullable(s1);
// 如果满足条件,就返回包含该值的Optional对象
Optional<String> f1 = o1.filter(opt -> "123".equals(opt));
// 如果不满足条件,就返回一个空的Optional对象
Optional<String> f2 = o1.filter(opt -> "234".equals(opt));
System.out.println(f1); // Optional[123]
System.out.println(f2); // Optional.empty
s1 = null;
Optional<String> o2 = Optional.ofNullable(s1);
// 如果不包含对象或不满足条件,就返回一个空的Optional对象
Optional<String> f3 = o2.filter(opt -> "123".equals(opt));
System.out.println(f3); // Optional.empty
}
public static void main(String[] args){
String s1 = new String("123");
Optional<String> o1 = Optional.ofNullable(s1);
// 如果值存在,就对该值执行提供的mapping函数调用
Optional<Integer> m1 = o1.map(opt -> opt.length());
System.out.println(m1); // Optional[3]
s1 = null;
Optional<String> o2 = Optional.ofNullable(s1);
// 如果值不存在,就返回一个空的Optional对象
Optional<Integer> m2 = o2.map(opt -> opt.length());
System.out.println(m2); // Optional.empty
}
public static void main(String[] args){
String s1 = new String("123");
Optional<String> o1 = Optional.ofNullable(s1);
// map方法,可能会形成嵌套关系
Optional<Optional<String>> m1 = o1.map(opt -> Optional.ofNullable(opt));
// 使用flatMap方法,会自动拆解嵌套关系
Optional<String> s = o1.flatMap(opt -> Optional.ofNullable(opt));
}
通过Teacher获取其下Student的姓名
public static void main(String[] args){
Teacher t = new Teacher();
// 以下代码很容易空指针异常,有可能t为null,有可能t.getStu为null,所以不安全
// t.getStu().getName();
// 此时的Teacher一定不为null
Teacher teacher = Optional.ofNullable(t).orElse(new Teacher());
Student stu = teacher.getStu();
// 此时的Student一定不为null
Student student = Optional.ofNullable(stu).orElse(new Student());
String name = student.getName();
System.out.println(name);
}
class Teacher{
private Student stu;
public Student getStu() {
return stu;
}
public void setStu(Student stu) {
this.stu = stu;
}
}
class Student{
private String name;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
}
假设你现在有一个HashMap类型的数据,你需要从这个map中读取一个值,该值是以秒为单位计量的一段时间,由于时间必须是整数。如果给定属性对应的值是一个代表正整数的字符串,就返回该整数值,任何其他的情况都返回0
public class Test{
public static void main(String[] args){
// 假设你只有这个map,只知道key,不知道里面的value
HashMap<String, Object> map = new HashMap<>();
map.put("aaa","3");
map.put("bbb","true");
map.put("bbb","-1");
System.out.println(readMap(map, "aaa")); // 3
System.out.println(readMap(map, "bbb")); // 0
System.out.println(readMap(map, "ccc")); // 0
}
// jdk1.8之前写法 安全,但麻烦
public static int readMap(Map map, String key){
if(map != null){
// 知道值肯定为String类型,
String s = (String) map.get(key);
// 如果值为null或不是一个可以转为Integer类型的字符串,会异常,这里catch住
try {
int i = Integer.parseInt(s);
if(i > 0){
return i;
}
}catch (NumberFormatException e){
}
}
// 如果没有返回i,其他情况就返回0
return 0;
}
}
public class Test{
public static void main(String[] args){
// 假设你只有这个map,只知道key,不知道里面的value
HashMap<String, Object> map = new HashMap<>();
map.put("aaa","3");
map.put("bbb","true");
map.put("bbb","-1");
System.out.println(readMap(map, "aaa")); // 3
System.out.println(readMap(map, "bbb")); // 0
System.out.println(readMap(map, "ccc")); // 0
}
// jdk1.8之前写法 安全,但麻烦
public static int readMap(Map map, String key){
if(map != null){
// 先把获取到的值放到Optional类里面
return Optional.ofNullable((String)map.get(key))
// 然后通过flatMap方法,把获取到的返回值拆解出来
// 此处使用map将会返回Optional>
// 使用flatMap会返回Optional,方便后续处理数据
.flatMap(Test::stringToInteger)
// 过滤,获取到的值,如果满足条件,则返回i,不走orElse
.filter(i -> i>0)
// 如果filter内方法执行为false,则返回Optional.empty,则返回orElse里面的内容
.orElse(0);
}
return 0;
}
/*
可以将多个类似的方法封装到一个工具类中,称之为OptionalUtil
通过这种方式,以后就能直接调用OptionalUtil.stringToInteger方法
将String转换为Optional对象。
*/
public static Optional<Integer> stringToInteger(String s){
try {
return Optional.of(Integer.parseInt(s));
}catch (NumberFormatException e){
return Optional.empty();
}
}
}
java.util.Optional
,对存在或缺失的变量值进行建模Optional.empty、Optional.of以及Optional.ofNullable
创建Optional对象map、flatMap、filter
它们在概念上与Stream类中对应的方法十分相似