Optional类
Optional
1、简单概述
jdk8中提供了一个很特别的类:Optional类,这个类设计的初衷是为了来解决臭名昭著的NPE问题。下面我们带着这些方法来进行操作
Optional类的方法总结如下所示:
方法 | 描述 |
---|---|
empty | 返回一个空的Optional类实例 |
of | 将对象封装到Optional类中去,要求对象不能够为空,否则返回一个NullPointerException |
ofNullable | 获取得到Optional类封装的对象,如果对象为空,那么返回一个空实例,如果不为空,返回一个封装了对象的Optional实例 |
filter | 如果值存在而且能够满足提供的谓词,就返回包含该值的Optional对象,否则返回一个空的Optional类实例 |
map | 如果Optional类封装的值存在,那么执行map函数中定义的内容 |
flatMap | 如果该值存在,通过Function函数,返回一个Optional类型的值,否则返回一个空的Optional类实例 |
get | 如果值存在,那么将Optional类实例封装的值返回,否则将会抛出NoSuchElementException异常 |
isPresent | 如果存在封装的对象,那么返回true;如果不存在,那么返回false |
ifPresent | 如果存在封装的对象,那么执行后面的消费方法;如果不存在,那么不做任何事情 |
orElse | 如果Optional实例对象是empty,那么使用默认的值来代替 |
orElseGet | 如果有值,则获取得到将其返回;如果没有值,那么将会使用指定的函数生成的值 |
orElseThrow | 如果有值将其进行返回,否则抛出一个指定的异常 |
先分别对这些方法来进行测试,然后做一个总结。
2、方法测试
2.1、of方法
@Test
public void testOne(){
String str = null;
Optional.of(str);
}
首先先写到这里,那么这里肯定会报错。看下源码:
public static <T> Optional<T> of(T value) {
return new Optional<>(value);
}
// 代码执行到这里来,那么就意味着这里要求一定不能够是空,否则就会报出NPE异常
private Optional(T value) {
this.value = Objects.requireNonNull(value);
}
public static <T> T requireNonNull(T obj) {
if (obj == null)
throw new NullPointerException();
return obj;
}
所以从这里可以看到,如果不能够保证这里的Value是否为null,那么就不要使用这个方法。这个方法仅仅说是返回了一个Optional
所以引入了下面的ofNullable方法
2.2、ofNullable方法
字面意思是可能为null,这个方法就是为了满足上面的方法而来的。我们不知道value是不是为null的情形,可以来使用这个方法来进行解决。
@Test
public void testThree(){
//为指定的值创建Optional对象,不管所传入的值为null不为null,创建的时候都不会报错
Optional<String> nullOptional1 = Optional.ofNullable(null);
Optional<String> nullOptional2 = Optional.ofNullable("lisi");
}
那么看下源码:
public static <T> Optional<T> ofNullable(T value) {
return value == null ? empty() : of(value);
}
如果是空的,那么就返回一个Optional类型的空对象,也就是说值为空的,拿到这个对象毫无意义。
如果不是空的,那么返回一个包装了这个value的Optioal对象,这个value是一个Optional对象,但是这个对象里面的值是null。
public static<T> Optional<T> empty() {
@SuppressWarnings("unchecked")
Optional<T> t = (Optional<T>) EMPTY;
return t;
}
// 调用了empty方法之后,返回了一个Optional对象,Optional对象中的value是一个null
private static final Optional<?> EMPTY = new Optional<>();
private Optional() {
this.value = null;
}
但是获取得到了这样的一个Optional类对象之后,肯定是需要来对里面的值来进行操作的。那么如何来确定这个值是不是null,这是一个问题。所以针对这个对象引入了下面的方法
2.3、ifPresent方法
字面意思是如果有值,该怎么来进行操作。
@Test
public void testOne(){
String str = "null";
Optional<String> str1 = Optional.ofNullable(str);
// 如果值不是为空的,那么就可以直接在这个里面来进行处理
str1.ifPresent(s -> System.out.println(str+"hello"));
}
如果不是空的,那么就进行ifPresent方法中的操作。这里可以看下对应的源码:
public void ifPresent(Consumer<? super T> consumer) {
if (value != null)
consumer.accept(value);
}
看到了这个Consumer接口,就什么都明白了。一种消费型方法,没有返回值的。
但是这个时候应该考虑到另外一个问题,如果说这个value是空的,那么又该来如何进行操作?
这又引出来了另外一个方法orElse
2.4、orElse方法
字面意思否则就会怎么样?
看一段代码:
@Test
public void testOne(){
Optional<Object> empty = Optional.empty();
System.out.println(empty.orElse("hello"));
}
如果Optional对象中的值是空的,那么该怎么来进行处理??这里使用了orElse方法来给value进行赋值,并返回来这个值。看下源码:
public T orElse(T other) {
return value != null ? value : other;
}
可以看到如果值不为空,那么就返回Optional这个对象中的值;如果是空的,那么就返回补偿代码补偿的值。
那么再来一段代码:
@Test
public void testOne(){
Student student = Student.builder().id(1).name("hello").build();
student.setId(Optional.ofNullable(student.getId()).orElse(12));
System.out.println(student); // Student(id=1, name=hello)
}
那么继续,下面接着不需要来对id来进行赋值:
@Test
public void testOne(){
Student student = Student.builder().name("hello").build();
System.out.println(student.getId()==null);
student.setId(Optional.ofNullable(student.getId()).orElse(12));
System.out.println(student); // Student(id=12, name=hello)
}
这个方法侧重于对一个Optional
Optional.ofNullable(student.getId()).orElse(12)
看下对应的源码:
// 这里返回的是一个empty(),也就是Optional<null>对象
public static <T> Optional<T> ofNullable(T value) {
return value == null ? empty() : of(value);
}
// 然后调用orElse方法,这个对象就是Optional<null>中的value=null,那么这里走other,然后返回
public T orElse(T other) {
return value != null ? value : other;
}
所以得到了最终的值之后,然后返回回去,重新给id赋值。
2.5、orElseGet方法
再拿上面的案例来举例子:
@Test
public void testFour(){
Student student = Student.builder().id(1).name("hello").build();
Integer integer = Optional.ofNullable(student.getId()).orElseGet(() -> 2);
System.out.println(integer); // 1
}
那么把这里的id去掉:
@Test
public void testFour(){
Student student = Student.builder().name("hello").build();
Integer integer = Optional.ofNullable(student.getId()).orElseGet(() -> 2);
System.out.println(integer); // 2
}
这里可以发生,当id是空的时候,即使返回的对象是一个Optional
那么和orElse源码来进行对比一下:
orElseGet
public T orElseGet(Supplier<? extends T> other) {
return value != null ? value : other.get();
}
orElse:
public T orElse(T other) {
return value != null ? value : other;
}
但是这里用了一个Supplier接口来进行操作,感觉和orElse差不多的存在。可能是没有用到具体的场景吧。
2.6 orElseThrow
字面意思"不然抛出一个异常"。
demo1:
// 以前的参数校验:
if(null==xx){
throw new RuntimeException("参数为空");
}
// 使用Optional
Optional.ofNullable(xx).orElseThrow(n->{
return New RuntimeExeception("参数为空");
})
demo2:
Optional<String> stringOptional = Optional.of("张三");
System.out.println(stringOptional.orElseThrow(CustomException::new));
Optional<String> emptyOptional = Optional.empty();
System.out.println(emptyOptional.orElseThrow(CustomException::new));
private static class CustomException extends RuntimeException {
private static final long serialVersionUID = -4399699891687593264L;
public CustomException() {
super("参数为空");
}
public CustomException(String message) {
super(message);
}
}
这里自定义异常的时候可以来进行使用。
2.7、isPresent方法
是否有这个value值?这里只是用来做一个判断而已。
@Test
public void testFour(){
Student student = Student.builder().name("hello").build();
boolean present = Optional.ofNullable(student).isPresent();
System.out.println(present); // true
}
再看一下:
@Test
public void testFour(){
Student student = Student.builder().id(1).name("hello").build();
boolean present = Optional.ofNullable(student).isPresent();
System.out.println(present); // false
}
这里仅仅是用来做一个判断的条件而已。
2.8、filter方法
过滤方法,看到了这个方法不禁想起来了Stream流中的filter方法,二者是相同的存在。直接看下源码即可。
public Optional<T> filter(Predicate<? super T> predicate) {
// 看到了这句话就不想往下看了。如果是空,直接跑出来空指针异常。
Objects.requireNonNull(predicate);
if (!isPresent())
return this;
else
return predicate.test(value) ? this : empty();
}
可以看到这里也是对非空值的处理:
Optional<String> stringOptional = Optional.of("zhangsan");
System.out.println(stringOptional.filter(e -> e.length() > 5).orElse("王五"));
stringOptional = Optional.empty();
System.out.println(stringOptional.filter(e -> e.length() > 5).orElse("lisi"));
2.9、map方法
如果有值,那么就执行对应的map中的函数
//map方法执行传入的lambda表达式参数对Optional实例的值进行修改,修改后的返回值仍然是一个Optional对象
Optional<String> stringOptional = Optional.of("zhangsan");
System.out.println(stringOptional.map(e -> e.toUpperCase()).orElse("失败"));
stringOptional = Optional.empty();
System.out.println(stringOptional.map(e -> e.toUpperCase()).orElse("失败"));
2.10、flatMap方法
如果创建的Optional中的值存在,就对该值执行提供的Function函数调用,返回一个Optional类型的值,否
则就返回一个空的Optional对象。flatMap与map(Funtion)方法类似,区别在于flatMap中的mapper返回
值必须是Optional,map方法的mapping函数返回值可以是任何类型T。调用结束时,flatMap不会对结果
用Optional封装。
//map方法中的lambda表达式返回值可以是任意类型,在map函数返回之前会包装为Optional。
//但flatMap方法中的lambda表达式返回值必须是Optionl实例
Optional<String> stringOptional = Optional.of("zhangsan");
System.out.println(stringOptional.flatMap(e -> Optional.of("lisi")).orElse("失败"));
stringOptional = Optional.empty();
System.out.println(stringOptional.flatMap(e -> Optional.empty()).orElse("失败"));
各自的侧重点不同而已。
3、图解
3.0、Optional空对象
private static final Optional<?> EMPTY = new Optional<>();
private Optional() {
this.value = null;
}
可以看到这里仅仅只是创建了一个Optional对象,而内部的value是为null的。
而optional类对象主要是用来操作该对象的value值。
3.1、of方法图
public static <T> Optional<T> of(T value) {
return new Optional<>(value);
}
private Optional(T value) {
this.value = Objects.requireNonNull(value);
}
3.2、ofNullable方法图
public static <T> Optional<T> ofNullable(T value) {
return value == null ? empty() : of(value);
}
那么对照着上面的两种情况,就对应着两个对象:
可能是这两个之间的任何一种情况,所以我们需要来对后面的值来做处理。
3.3、ifPresent方法图
public void ifPresent(Consumer<? super T> consumer) {
if (value != null)
consumer.accept(value);
}
如果值不是空的,那么就会对这里面的值来进行操作。
3.4、orElse方法
public T orElse(T other) {
return value != null ? value : other;
}
对应的图解:
如果是空的,那么走orElse中的逻辑。
3.5、orElseGet方法图
public T orElseGet(Supplier<? extends T> other) {
return value != null ? value : other.get();
}
3.6、orElseThrow方法图
public <X extends Throwable> T orElseThrow(Supplier<? extends X> exceptionSupplier) throws X {
if (value != null) {
return value;
} else {
throw exceptionSupplier.get();
}
}
如果对象是为空的,那么就跑出来异常,而这个异常是自己来定义的,实现Throwable接口及其子类即可。只要是空,抛出对应的异常即可。