optional

JAVA8新特性之Optional

一、Optional

  1. 根据jdk8官方文档,可以知道,该根据类存在于java.util下的final类,是官方提供的工具类;
  2. Optional类是java8为了解决null值判断问题,借鉴google guava类库的Optional类而引入的一个同名Optional类,使用Optional类可以避免显示的null值判断,避免null导致的NPE(NullPointException);
  3. 学习了解Optional创建对象和常用方法的使用;
  4. 使用Optional开发时要注意正确使用Optional的“姿势”,特别注意不要使用3.2节提到的错误示范,谨慎使用isPresent()和get()方法,尽量多使用map()、filter()、orElse()等方法来发挥Optional的作用;
  5. 不过采用这种Optional链式编程,虽然代码优雅了。但是,逻辑性没那么明显,可读性有所降低;

二、Optional详解

// 1、创建一个包装对象值为空的Optional对象
Optional optStr = Optional.empty();
// 2、创建包装对象值非空的Optional对象
Optional optStr1 = Optional.of(“optional”);
// 3、创建包装对象值允许为空的Optional对象
Optional optStr2 = Optional.ofNullable(null);

//4、常用方法:get、isPresent、ifPresent、filter、map、flatMap、orElse、orElseGet、orElseThrow

序号 方法 方法说明
1 private Optional() 无参构造,构造一个空Optional
2 private Optional(T value) 根据传入的非空value构建Optional
3 public static Optional empty() 返回一个空的Optional,该实例的value为空
4 public static Optional of(T value) 根据传入的非空value构建Optional,与Optional(T value)方法作用相同
5 public static Optional ofNullable(T value) 与of(T value)方法不同的是,ofNullable(T value)允许你传入一个空的value,当传入的是空值时其创建一个空Optional,当传入的value非空时,与of()作用相同
6 public T get() 返回Optional的值,如果容器为空,则抛出NoSuchElementException异常
7 public boolean isPresent() 判断当家Optional是否已设置了值
8 public void ifPresent(Consumer consumer) 判断当家Optional是否已设置了值,如果有值,则调用Consumer函数式接口进行处理
9 public Optional filter(Predicate predicate) 如果设置了值,且满足Predicate的判断条件,则返回该Optional,否则返回一个空的Optional
10 public Optional map(Function mapper) 如果Optional设置了value,则调用Function对值进行处理,并返回包含处理后值的Optional,否则返回空Optional
11 public Optional flatMap(Function> mapper) 与map()方法类型,不同的是它的mapper结果已经是一个Optional,不需要再对结果进行包装
12 public T orElse(T other) 如果Optional值不为空,则返回该值,否则返回other
13 public T orElseGet(Supplier other) 如果Optional值不为空,则返回该值,否则根据other另外生成一个
14 public T orElseThrow(Supplier exceptionSupplier)throws X 如果Optional值不为空,则返回该值,否则通过supplier抛出一个异常

三、Optional源码

Optional类源码,重写了Object类中的equals、hashcode、toString方法

package java.util;

import java.util.function.Consumer;
import java.util.function.Function;
import java.util.function.Predicate;
import java.util.function.Supplier;

public final class Optional<T> {

    private static final Optional<?> EMPTY = new Optional<>();

    private final T value;

    private Optional() {
        this.value = null;
    }

    public static<T> Optional<T> empty() {
        @SuppressWarnings("unchecked")
        Optional<T> t = (Optional<T>) EMPTY;
        return t;
    }

    private Optional(T value) {
        this.value = Objects.requireNonNull(value);
    }

    public static <T> Optional<T> of(T value) {
        return new Optional<>(value);
    }

    public static <T> Optional<T> ofNullable(T value) {
        return value == null ? empty() : of(value);
    }

    //get()方法主要用于返回包装对象的实际值,但是如果包装对象值为null,会抛出NoSuchElementException异常
    public T get() {
        if (value == null) {
            throw new NoSuchElementException("No value present");
        }
        return value;
    }

    //isPresent()方法用于判断包装对象的值是否非空
    public boolean isPresent() {
        return value != null;
    }

    //ifPresent()方法接受一个Consumer对象(消费函数),如果包装对象的值非空,运行Consumer对象的accept()方法
    public void ifPresent(Consumer<? super T> consumer) {
        if (value != null)
            consumer.accept(value);
    }

    //filter()方法接受参数为Predicate对象,用于对Optional对象进行过滤,如果符合Predicate的条件,返回Optional对象本身,否则返回一个空的Optional对象
    public Optional<T> filter(Predicate<? super T> predicate) {
        Objects.requireNonNull(predicate);
        if (!isPresent())
            return this;
        else
            return predicate.test(value) ? this : empty();
    }

    //map()方法的参数为Function(函数式接口)对象,map()方法将Optional中的包装对象用Function函数进行运算,并包装成新的Optional对象(包装对象的类型可能改变)
    public<U> Optional<U> map(Function<? super T, ? extends U> mapper) {
        Objects.requireNonNull(mapper);
        if (!isPresent())
            return empty();
        else {
            return Optional.ofNullable(mapper.apply(value));
        }
    }

    //跟map()方法不同的是,入参Function函数的返回值类型为Optional类型,而不是U类型,这样flatMap()能将一个二维的Optional对象映射成一个一维的对象
    public<U> Optional<U> flatMap(Function<? super T, Optional<U>> mapper) {
        Objects.requireNonNull(mapper);
        if (!isPresent())
            return empty();
        else {
            return Objects.requireNonNull(mapper.apply(value));
        }
    }

    //orElse()方法功能比较简单,即如果包装对象值非空,返回包装对象值,否则返回入参other的值(默认值)
    public T orElse(T other) {
        return value != null ? value : other;
    }

    //orElseGet()方法与orElse()方法类似,区别在于orElseGet()方法的入参为一个Supplier对象,用Supplier对象的get()方法的返回值作为默认值
    public T orElseGet(Supplier<? extends T> other) {
        return value != null ? value : other.get();
    }

    //orElseThrow()方法其实与orElseGet()方法非常相似了,入参都是Supplier对象,只不过orElseThrow()的Supplier对象必须返回一个Throwable异常,并在orElseThrow()中将异常抛出。orElseThrow()方法适用于包装对象值为空时需要抛出特定异常的场景
    public <X extends Throwable> T orElseThrow(Supplier<? extends X> exceptionSupplier) throws X {
        if (value != null) {
            return value;
        } else {
            throw exceptionSupplier.get();
        }
    }

    @Override
    public boolean equals(Object obj) {
        if (this == obj) {
            return true;
        }
        if (!(obj instanceof Optional)) {
            return false;
        }
        Optional<?> other = (Optional<?>) obj;
        return Objects.equals(value, other.value);
    }

    @Override
    public int hashCode() {
        return Objects.hashCode(value);
    }

    @Override
    public String toString() {
        return value != null
            ? String.format("Optional[%s]", value)
            : "Optional.empty";
    }
}

四、Optional方法常用实列

1、Optional(T value),empty(),of(T value),ofNullable(T value)

Optional(T value),empty(),of(T value),ofNullable(T value)
这四个函数之间具有相关性,因此放在一组进行记忆。
先说明一下,Optional(T value),即构造函数,它是private权限的,不能由外部调用的。其余三个函数是public权限,供我们所调用。
那么,Optional的本质,就是内部储存了一个真实的值,在构造的时候,就直接判断其值是否为空

2、orElse(T other),orElseGet(Supplier other)和orElseThrow(Supplier exceptionSupplier)

orElse(T other),orElseGet(Supplier other)和orElseThrow(Supplier exceptionSupplier)
这三个函数放一组进行记忆,都是在构造函数传入的value值为null时,进行调用的。orElse和orElseGet的用法如下所示,相当于value值为null时,给予一个默认值:

@Test
public void test() {
    User user = null;
    user = Optional.ofNullable(user).orElse(createUser());
    user = Optional.ofNullable(user).orElseGet(() -> createUser());
    
}
public User createUser(){
    User user = new User();
    user.setName("zhangsan");
    return user;
}

这两个函数的区别:当user值不为null时,orElse函数依然会执行createUser()方法,而orElseGet函数并不会执行createUser()方法,大家可自行测试。
至于orElseThrow,就是value值为null时,直接抛一个异常出去,用法如下所示

User user = null;
Optional.ofNullable(user).orElseThrow(()->new Exception("用户不存在"));			

3、map(Function mapper)和flatMap(Function> mapper)

map(Function mapper)和flatMap(Function> mapper)
这两个函数放在一组记忆,这两个函数做的是转换值的操作。

这两个函数,在函数体上没什么区别。唯一区别的就是入参,map函数所接受的入参类型为Function,而flapMap的入参类型为Function>

用map时,实体类User这样写:

获取name写法:String city = Optional.ofNullable(user).map(u-> u.getName()).get();

public class User {
    private String name;
    public String getName() {
        return name;
    }
}

用flatMap时,实体类User这样写:

获取name写法:String city = Optional.ofNullable(user).flatMap(u-> u.getName()).get();

public class User {
    private String name;
    public Optional<String> getName() {
        return Optional.ofNullable(name);
    }
}

4、isPresent()ifPresent(Consumer consumer)

isPresent()和ifPresent(Consumer consumer)
这两个函数放在一起记忆,isPresent即判断value值是否为空,而ifPresent就是在value值不为空时,做一些操作。

isPresent()千外不要下面这样写:

if (user != null){
   // TODO: do something
}
写为:
Optional<User> user = Optional.ofNullable(user);
if (user.isPresent()){
   user.get().setUsername("小明")
}else{
    throw new NullPointException();
}
因为这样写,代码结构依然丑陋。
正确写法:
Optional.ofNullable(user).ifPresent(u->{u.setUsername("小明")});

fPresent(Consumer consumer)

public static void printName(Student student)
    {
        Optional.ofNullable(student).ifPresent(u ->  System.out.println("The student name is : " + u.getName()));
    }

5、filter(Predicate predicate)

filter 方法接受一个 Predicate 来对 Optional 中包含的值进行过滤,如果包含的值满足条件,那么还是返回这个 Optional;否则返回 Optional.empty。

如上下所示,如果user的name的长度是小于6的,则返回。如果是大于6的,则返回一个EMPTY对象

Optional<User> user = Optional.ofNullable(user).filter(u -> u.getName().length()<6);

五、Optional扩展

1、使用Optional避免空指针

在我们日常开发过程中不可避免地会遇到空指针问题,在以前,出现空指针问题,我们通常需要进行调试等方式才能最终定位到具体位置,尤其是在分布式系统服务之间的调用,问题更难定位。在使用Optional后,我们可以将接受到的参数对象进行包装,比如,订单服务要调用商品服务的一个接口,并将商品信息通过参数传入,这时候,传入的商品参数可能直接传入的就是null,这时,商品方法可以使用Optional.of(T)对传入的对象进行包装,如果T为空,则会直接抛出空指针异常,我们看到异常信息就能立即知道发生空指针的原因是参数T为空;或者,当传入的参数为空时,我们可以使用Optional.orElse()或Optional.orElseGet()方法生成一个默认的实例,再进行后续的操作。

下面再看个具体例子:在User类中有个Address类,在Address类中有个Street类,Street类中有streetName属性,现在的需求是:根据传入的User实例,获取对应的streetName,如果User为null或Address为null或Street为null,返回“nothing found”,否则返回对应的streetName。

Optional写法:
——实体类:
1 @Data
2 public class User {
3     private String name;
4     private Integer age;
5     private Optional<Address> address = Optional.empty();
6 }
1 @Data
2 public class Address {
3     private Optional<Street> street = Optional.empty();
4 }
1 @Data
2 public class Street {
3     private String streetName;
4     private Integer streetNo;
5 }
——获取streetName
1 public String getUserSteetName(User user) {
2 
3     Optional<User> userOptional = Optional.ofNullable(user);
4     final String streetName = userOptional.orElse(new User()).getAddress().orElse(new Address()).getStreet().orElse(new Street()).getStreetName();
5     return StringUtils.isEmpty(streetName) ? "nothing found" : streetName;
6 }

2、取实体类中属性为对象的属性

之前写法

public String getCity(User user)  throws Exception{
        if(user!=null){
            if(user.getAddress()!=null){
                Address address = user.getAddress();
                if(address.getCity()!=null){
                    return address.getCity();
                }
            }
        }
        throw new Excpetion("取值错误"); 
    }

Optional写法:

public String getCity(User user) throws Exception{
    return Optional.ofNullable(user)
                   .map(u-> u.getAddress())
                   .map(a->a.getCity())
                   .orElseThrow(()->new Exception("取指错误"));
}

3、获取返回存在某属性的对象,不存在创建属性返回对象

之前写法

public User getUser(User user) throws Exception{
    if(user!=null){
        String name = user.getName();
        if("zhangsan".equals(name)){
            return user;
        }
    }else{
        user = new User();
        user.setName("zhangsan");
        return user;
    }
}

Optional写法:

public User getUser(User user) {
    return Optional.ofNullable(user)
                   .filter(u->"zhangsan".equals(u.getName()))
                   .orElseGet(()-> {
                        User user1 = new User();
                        user1.setName("zhangsan");
                        return user1;
                   });
}

4、项目中实列

之前写法

  public String test0(AlarmAllParmeter alarmAllParmeter) {
        String errorResult = "";
        if (null != alarmAllParmeter) {
            Integer alarmId = alarmAllParmeter.getAlarmEventInputId();
            if (null != alarmId) {
                AlarmEventInput alarmEventInput = alarmEventInputService.get(alarmId);
                if (null != alarmEventInput) {
                    String alarmName = alarmEventInput.getAlarmName();
                    int alarmType = alarmEventInput.getAlarmType();
                    return String.valueOf(alarmType) + "-" + alarmName;
                } else {
                    return errorResult;
                }
            } else {
                return errorResult;
            }
        } else {
            return errorResult;
        }
    }

Option的错误写法:

    public String test1(AlarmAllParmeter alarmAllParmeter){
        String errorResult = "";
        Optional<AlarmAllParmeter> op = Optional.ofNullable(alarmAllParmeter);
        if(op.isPresent()){
            Integer alarmId = op.get().getAlarmEventInputId();
            Optional<Integer> op1 = Optional.ofNullable(alarmId);
            if(op1.isPresent()){
                AlarmEventInput alarmEventInput = alarmEventInputService.get(op1.get());
                Optional<AlarmEventInput> op2 = Optional.ofNullable(alarmEventInput);
                if (op2.isPresent()) {
                    String alarmName = alarmEventInput.getAlarmName();
                    int alarmType = alarmEventInput.getAlarmType();
                    return String.valueOf(alarmType) + "-" + alarmName;
                } else {
                    return errorResult;
                }
            }
            else {
                return errorResult;
            }
        }
        else {
            return errorResult;
        }
    }

Optional正确写法:

    public String test2(AlarmAllParmeter alarmAllParmeter){
        return Optional.ofNullable(alarmAllParmeter)
                       .map(a -> a.getAlarmEventInputId())
                       .map(a -> alarmEventInputService.get(a))
                       .map(a -> String.valueOf(a.getAlarmType())+"-"+a.getAlarmName())
                       .orElse("");
    }

你可能感兴趣的:(Java,java)