Java8 Optional类用法


Java8 提供了Optional类来优化判空写法。
Optional的本质,就是内部储存了一个真实的值,在构造的时候,就直接判断其值是否为空。

API介绍

第一组

这四个函数之间具有相关性,因此放在一起;

Optional(T value)

Optional(T value),即构造函数,它是private权限的,不能由外部调用的。

	/**
     * Constructs an instance with the described value.
     *
     * @param value the non-{@code null} value to describe
     * @throws NullPointerException if value is {@code null}
     */
    private Optional(T value) {
        this.value = Objects.requireNonNull(value);
    }

其余三个函数是public权限,供我们所调用。

empty()

Optional类内部还维护一个value为null的对象,大概就是长下面这样的:

public final class Optional<T> {
    //省略....
    private static final Optional<?> EMPTY = new Optional<>();
    private Optional() {
        this.value = null;
    }
    //省略...
    public static<T> Optional<T> empty() {
        @SuppressWarnings("unchecked")
        Optional<T> t = (Optional<T>) EMPTY;
        return t;
    }
}

empty() 的作用就是返回EMPTY对象。

of(T value)

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

of(T value)函数内部调用了构造函数。根据构造函数的源码我们可以得出两个结论:

  • 通过of(T value)函数所构造出的Optional对象,当Value值为空时,依然会报NullPointerException。
  • 通过of(T value)函数所构造出的Optional对象,当Value值不为空时,能正常构造Optional对象。

使用场景:
当我们在运行过程中,不想隐藏NullPointerException。而是要立即报告,这种情况下就用Of函数。

ofNullable(T value)

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

相比较of(T value)的区别就是:
当value值为null时,of(T value)会报NullPointerException异常;ofNullable(T value)不会throw Exception,ofNullable(T value)直接返回一个EMPTY对象。

第二组

这三个函数放一组进行记忆,都是在构造函数传入的value值为null时,进行调用的。

orElse(T other),orElseGet(Supplier other)

详细API源码如下:

/**
 * Return the value if present, otherwise return {@code other}.
 *
 * @param other the value to be returned if there is no value present, may
 * be null
 * @return the value, if present, otherwise {@code other}
 */
public T orElse(T other) {
    return value != null ? value : other;
}

/**
 * Return the value if present, otherwise invoke {@code other} and return
 * the result of that invocation.
 *
 * @param other a {@code Supplier} whose result is returned if no value
 * is present
 * @return the value if present otherwise the result of {@code other.get()}
 * @throws NullPointerException if value is not present and {@code other} is
 * null
 */
public T orElseGet(Supplier<? extends T> other) {
    return value != null ? value : other.get();
}

orElse和orElseGet的用法如下所示,相当于value值为null时,给予一个默认值:

public class OptionalTest {

    public static void main(String[] args) throws Exception {
        // Person person = null;
        Person person = new Person();
        Person person1 = Optional.ofNullable(person).orElse(creatPerson());
        System.out.println(person1);
        Person person2 = Optional.ofNullable(person).orElseGet(() -> creatPerson());
        System.out.println(person2);

        Optional.ofNullable(person).orElseThrow(() -> new Exception("没有此人"));
    }

    public static Person creatPerson() {
        Person person = new Person();
        person.setName("Tom");
        person.setAge(18);
        person.setNickName("cat");
        System.out.println("create person");

        return person;
    }
}

这两个函数的区别:

上面的测试可以发现。
当person值不为null时,orElse函数依然会执行creatPerson()方法,
而orElseGet函数并不会执行creatPerson()方法;

orElseThrow(Supplier exceptionSupplier)

orElseThrow,就是value值为null时,直接抛一个异常出去:

/**
 * Return the contained value, if present, otherwise throw an exception
 * to be created by the provided supplier.
 *
 * @apiNote A method reference to the exception constructor with an empty
 * argument list can be used as the supplier. For example,
 * {@code IllegalStateException::new}
 *
 * @param <X> Type of the exception to be thrown
 * @param exceptionSupplier The supplier which will return the exception to
 * be thrown
 * @return the present value
 * @throws X if there is no value present
 * @throws NullPointerException if no value is present and
 * {@code exceptionSupplier} is null
 */
public <X extends Throwable> T orElseThrow(Supplier<? extends X> exceptionSupplier) throws X {
    if (value != null) {
        return value;
    } else {
        throw exceptionSupplier.get();
    }
}

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

两个函数做的是转换值的操作:

public final class Optional<T> {
    //省略....
     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));
        }
    }
    //省略...
     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));
        }
    }
}

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

示例1:

public class OptionalMapFilterDemo {
    public static void main(String[] args) {
        String password = "password";
        Optional<String>  opt = Optional.ofNullable(password);

        Predicate<String> len6 = pwd -> pwd.length() > 6;
        Predicate<String> len10 = pwd -> pwd.length() < 10;
        Predicate<String> eq = pwd -> pwd.equals("password");

        boolean result = opt.map(String::toLowerCase).filter(len6.and(len10 ).and(eq)).isPresent();
        System.out.println(result);
    }
}

示例2:

public class Person {

    private String name;
    private int age;
    private String nickName;

    public Person() {
    }

    public Person(String name, int age) {
        this.name = name;
        this.age = age;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public int getAge() {
        return age;
    }

    public void setAge(int age) {
        this.age = age;
    }

    public Optional<String> getNickName() {
        return Optional.ofNullable(nickName);
    }

    public void setNickName(String nickName) {
        this.nickName = nickName;
    }

    @Override
    public String toString() {
        return "Person{" +
                "name='" + name + '\'' +
                ", age=" + age +
                ", nickName='" + nickName + '\'' +
                '}';
    }
}
public static void testMap() {
    Person newPerson = creatPerson();
    Integer age = Optional.ofNullable(newPerson).map(e -> e.getAge()).get();
    System.out.println("newPerson age = " + age);
    
    String nickName = Optional.ofNullable(newPerson).flatMap(e -> e.getNickName()).get();
    System.out.println("newPerson nickName = " + nickName);
}

isPresent()和ifPresent(Consumer consumer)

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

filter(Predicate predicate)

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

案例

例子1

比如,在主程序中

以前写法

if(user!=null){
    dosomething(user);
}

java8写法

 Optional.ofNullable(user)
    .ifPresent(u->{
        dosomething(u);
});

例子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("取值错误");
    }

java8写法

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;
    }
}

java8写法

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;
                   });
}

采用这种链式编程,虽然代码优雅了。但是,逻辑性没那么明显,可读性有所降低,大家项目中看情况酌情使用。

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值