Optional的使用场景和其用法介绍
jdk1.8新增了一个Optional类,同时还增加了OptionalDouble、OptionalInt,OptionalLong用于对基本类型double、int、long的支持。
Optional的使用场景介绍
有时候我们需要从一个对象中获取其成员变量,甚至需要获取其成员变量的变量,通常为了避免产生空指针异常需要进行非空校验,当嵌套层次较多时存在多层嵌套的if非空判断,看如下代码,UserInfo表示用户,为了获取用户的曾祖父信息,我们使用了差不多用户了12行的代码,其中除了打印曾祖父信息之外其它的都是嵌套非空判断,代码显得很冗余笨拙:
public class OptionalTest {
static class UserInfo {
private int id;
private String name;
private UserInfo father;
public UserInfo(int id, String name, UserInfo father) {
this.id = id;
this.name = name;
this.father = father;
}
@Override
public String toString() {
return "UserInfo{" + "id=" + id + ", name='" + name + '\'' + '}';
}
}
public static void main(String[] args) {
UserInfo ui = new UserInfo(1, "张三", null);
UserInfo greatGrandFather = null;
if (ui != null) {
UserInfo father = ui.father;
if (father != null) {
UserInfo grandFather = father.father;
if (grandFather != null) {
greatGrandFather = grandFather.father;
}
}
}
if (greatGrandFather != null) {
System.out.println(greatGrandFather );
}
}
}
反之使用Optional来实现同样的判断将使得代码精简且优雅很多,只需一行代码即可,具体如下:
public static void main(String[] args) {
UserInfo ui = new UserInfo(1, "张三", null);
Optional.ofNullable(ui).map(u->u.father).map(u->u.father).map(u->u.father).ifPresent(System.out::println);
}
Optional的方法介绍
从上可知Optional主要用于对象可能为null的情况下,需要对对象进行相关操作时简化非空校验的一个容器,同时其相关操作后都会返回一个Optional对象可用于来连续方法调用。其构造器私有化不允许用户直接通过构造器获取Optional实例,因此Optional提供了两个静态方法用于获取对象的Optional实例。两个静态方法介绍如下:
- public static Optional of(T value):传入的value不允许为null,否则将抛出NullPointerException异常
- public static Optional ofNullable(T value):相比of该方法允许传入的value为null
获取到对象的封装对象后,我们就可以使用其提供的便捷方法了,介绍如下:
- public T get():获取原值,如果原值为null,将抛出NoSuchElementException异常
- public boolean isPresent():判断原值是否为null,为null返回false
- ofElse(T t):当原值为null时返回t,否则返回原值
上述几个方法是Optional提供的最基本的用法,下面对几个比较实用的方法进行示例
- public T orElseGet(Supplier<? extends T> other):当原值!=null时返回原值,否则返回other提供的值
public static void main(String[] args) {
UserInfo ui = new UserInfo(1, "张三", null);
//当ui == null式输出张三copy信息,否则输出张三信息
System.out.println(Optional.ofNullable(ui).orElseGet(()-> new UserInfo(11,"张三copy",null)));
}
- public T orElseThrow(Supplier<? extends X> exceptionSupplier) throws X:当原值!=null时返回原值,否则抛出由exceptionSupplier提供的异常对象
public static void main(String[] args) {
UserInfo ui = null;//new UserInfo(1, "张三", null);
//当ui==null时,将抛出IllegalStateException("值为null")异常,否则正常返回原值
System.out.println(Optional.ofNullable(ui).orElseThrow(()->new IllegalStateException("值为null")));
}
- public Optional filter(Predicate<? super T> predicate):原值过滤并返回一个原值可为null的Optional
public static void main(String[] args) {
UserInfo ui = new UserInfo(1, "张三", null);
//当ui的id>1时返回ui,否则返回null
System.out.println(Optional.ofNullable(ui).filter(u->u.id > 0).orElse(null));
}
- public Optional flatMap(Function<? super T, Optional> mapper):返回值由mapper自定义,要求mapper必须返回一个Optional对象。
public static void main(String[] args) {
UserInfo ui = new UserInfo(1, "张三", null);
//当ui.id==1时,将ui.name更改并返回一个可为空或不可谓空的Optional
System.out.println(Optional.of(ui).flatMap(u->{
if(u.id == 1)
u.name = "张三更改名字";
return Optional.of(u);//或则Optional.ofNullable(u),可自定义返回的Optional是否允许null值
}).orElse(null));
}
- public Optional map(Function<? super T, ? extends U> mapper):和flatMap功能相似,相对于flatMap,map默认返回一个ofNullable的Optional对象。
public static void main(String[] args) {
UserInfo ui = new UserInfo(1, "张三", null);
//当ui.id==1时,将ui.name更改并返回一个可为空的Optional
System.out.println(Optional.of(ui).map(u->{
if(u.id == 1)
u.name = "张三更改名字";
return u;//或则Optional.ofNullable(u),可自定义返回的Optional是否允许null值
}).orElse(null));
}
OptionalDouble、OptionalInt,OptionalLong介绍
当我们使用基本类型(int,double,long)作为方法入参时,有时候对于基本类型的值感到不解,比如对于对于如下方法:当annualProfit=0时到底表示收支相等还是一年根本没有任何收支信息?如果需要区分这这种情况如何区分?当然了你可以使用封装类型为null进行判断,但封装类型在使用时需要拆盒或装盒功能,如果计算量非常大时对性能还是有所影响的,这个时候可以使用OptionalDouble类来实现。
public void showAnnualProfit(double annualProfit){
System.out.println("个人年结余金额为:"+annualProfit);
}
OptionalDouble实现
public void showAnnualProfit(OptionalDouble annualProfit){
if(annualProfit.isPresent()){
System.out.println("个人年结余金额为:"+annualProfit.getAsDouble());
}else{
System.out.println("无个人收支信息");
}
}
OptionalInt,OptionalLong和OptionalDouble相差不大,从源码中可以看出他们没有Optional提供的一系列函数式入参对应的方法,基本类型不支持相应的泛型申明,同时也没必要对基本类型应用复杂的函数式功能。