OPTIONAL的学习与理解以及应用

在java1.8版中引入新特性是Optional类,用来解决程序员都会遇到的问题 空指针异常(NullPointerException)
Optional类型被当做更安全的一种方式,用来替代T类型的引用,但是它只有在正确的使用方式下才能更安全,接下来我们看看如果正确的使用。

使用OPTIONAL获取JSON或者对象中的数据

{
	"user": {
		"age": 20
		,"name": "Jim"
		,"address": {
			"province": "浙江省"
			,"postcode": "111111"
		}
   }
}

获取postcode的值

或获取对象的属性

user.getAddress().getProvince().getPostCode()

如果使用平常的方法,需要判断对每个值都继续判断,否则程序可能会出现空指针异常。

if (user != null) {
    address = user.getAddress();
    if (address != null) {
        province = address.getProvince();
        if (province != null) {
            postCode = province.getPostCode();
        }
    }
}

这样的代码就非常的冗余并且难以维护。
但是如果我们利用Optional则清晰并方便的获取到想要的属性值。

首先先创建 Optional的对象

调用empty() 方法创建一个空的Optional实例

Optional<User> empty = Optional.empty();
User user = empty.get();

调用此方法,程序会抛出了NoSuchElementException

Optional创建对象的方法有2种,

static <T> Optional<T> 		of(T value) 
static <T> Optional<T>		ofNullable(T value) 

这2钟创建方法有什么不同呢,

Optional<User> userOpt = Optional.of(user);

用of()方法创建时程序会抛出NullPointerException,还是没有摆脱空指针异常,所以当你明确知道对象不为null的时候才使用of()

Optional<User> userOpt = Optional.ofNullable(user);

如果使用ofNullable()则会产生一个空Optional

有了实例之后就可以访问对象里面的值

获取值的方法其中之一的是 T get(),get方法会在Optional值存在的时候获取对象其中的元素,但是在对象为Null的时候回抛出NoSuchElementException异常。
例如:

Optional<User> empty = Optional.empty();
User user = empty.get();

如果想避免这种情况的话需要先判断Optional对象是否有值。
利用boolean isPresent();

if (userOpt.isPresent()){
   User user =  userOpt.get();
}

如果需要对象可能空值,但是又需要程序继续进行,可以给默认值

T 		orElse(T other) 
T 		orElseGet(Supplier<? extends T> other)  
<X extends Throwable>           T orElseThrow(Supplier<? extends X> exceptionSupplier)  

api里面提供了3种方法
第一种的使用

User user=null;
User user2=new User();
User userObject = Optional.ofNullable(user).orElse(user2);

这里可以发现返回了一个默认值的user2
第二种的使用,是可以在参数中传递一个函数
例如:

User userObject  = Optional.ofNullable(user).orElseGet(() -> new User());
或
User userObject  = Optional.ofNullable(user).orElseGet(() -> user2);

orElse() 和 orElseGet() 的不同之处
看起来两者的使用好像很相似,但是大有不同

    User user=null;
    User user2=new User();
    System.out.println("orElse");
    User userObject = Optional.ofNullable(user).orElse(println());
    System.out.println("orElseGet");
    User userObject2 = Optional.ofNullable(user).orElseGet(()->println());

public User println(){
    System.out.println("被执行到");
    return new User();
}

执行结果:
orElse
被执行到
orElseGet
被执行到

如果不为空的情况

  User user=new User();
    User user2=new User();
    System.out.println("orElse");
    User userObject = Optional.ofNullable(user).orElse(println());
    System.out.println("orElseGet");
    User userObject2 = Optional.ofNullable(user).orElseGet(()->println());

执行结果:
orElse
被执行到
orElseGet

可以通过这里例子得出
不管前面判空是不是空,orElse都会执行,orElseGet则只在对象为空的时候才执行
使用orElseGet可以节省一个生成对象的性能,在高并发的情况下,可以节省大量的性能

在第三种备选值是可以在对象为空的时候抛出异常,但是同样里面可以写函数,自定义一些异常

值的转换和过滤

如果使用过stream流操作这块就非常熟悉了,optional 可以使用和stream中一样的方法来操作数据
我们先看下值的转换 map()和flatMap()

User user=new User("zhangsna",23);
String name=Optional.ofNullable(user).map(u->u.getUserName()).orElse("lisi");

使用flatMap()我们先写一个方法

static String Optional<String> getPosition(String name)() {  
          return Optional.ofNullable(name );
        }  

使用

String name = Optional.ofNullable(user)
      .flatMap(u -> u.getName()).orElse("lisi");

2者的结果是一样的
对于map和flatmap不同:
map是把结果自动封装成一个Optional,但是flatmap需要你自己去封装。
map的输出对应一个元素,必然是一个元素(null也是要返回),flatmap是0或者多个元素(为null的时候其实就是0个元素)。
这样flatmap的使用意义就在于,一般的java方法都是返回一个结果,但是对于结果数量不确定的时候,用map这种java方法的方式,是不太灵活的,所以引入了flatmap。

过滤值

除了转换值之外,Optional 类也提供了按条件“过滤”值的方法。
filter() 接受一个 Predicate 参数,返回测试结果为 true 的值。如果测试结果为 false,会返回一个空的 Optional。

User user=new User("zhangsna",23);
Optional<User> result =Optional.ofNullable(user).filter(u->u.getUserName()!=null&&u.getAge>0);

掌握了这些内容之后,我们可以开始实现我们最开始的需求

String result = Optional.ofNullable(user)
  .flatMap(User::getAddress)
  .flatMap(Address::getProvince)
  .map(Country::getPostCode)
  .orElse("default");

是不是代码,减少了很多冗余变得简洁清晰了很多呢

在结尾处贴一个java核心技术卷 II 第十一版中关于演示Optional的使用事例来供大家学习

public static void main(String[] args) throws IOException {
    Optional<User> empty = Optional.empty();
    User user = empty.get();

    Optional<User> userOpt = Optional.of(user);
    if (userOpt.isPresent()){
        userOpt.get();
    }

    String contents = new String(Files.readAllBytes(
            Paths.get("../gutenberg/alice30.txt")), StandardCharsets.UTF_8);
    List<String> wordList = Arrays.asList(contents.split("\\PL+"));

    Optional<String> optionalValue = wordList.stream()
            .filter(s -> s.contains("fred"))
            .findFirst();
    System.out.println(optionalValue.orElse("No word") + " contains fred");

    Optional<String> optionalString = Optional.empty();
    String result = optionalString.orElse("N/A");
    System.out.println("result: " + result);
    result = optionalString.orElseGet(() -> Locale.getDefault().getDisplayName());
    System.out.println("result: " + result);
    try {
        result = optionalString.orElseThrow(IllegalStateException::new);
        System.out.println("result: " + result);
    } catch (Throwable t) {
        t.printStackTrace();
    }

    optionalValue = wordList.stream()
            .filter(s -> s.contains("red"))
            .findFirst();
    optionalValue.ifPresent(s -> System.out.println(s + " contains red"));

    HashSet<String> results = new HashSet<String>();
    optionalValue.ifPresent(results::add);
    Optional<Boolean> added = optionalValue.map(results::add);
    System.out.println(added);

    System.out.println(inverse(4.0).flatMap(OptionalApi::squareRoot));
    System.out.println(inverse(-1.0).flatMap(OptionalApi::squareRoot));
    System.out.println(inverse(0.0).flatMap(OptionalApi::squareRoot));
    Optional<Double> result2 = Optional.of(-4.0)
            .flatMap(OptionalApi::inverse).flatMap(OptionalApi::squareRoot);
    System.out.println(result2);
}

public static Optional<Double> inverse(Double x) {
    return x == 0 ? Optional.empty() : Optional.of(1 / x);
}

public static Optional<Double> squareRoot(Double x) {
    return x < 0 ? Optional.empty() : Optional.of(Math.sqrt(x));
}
  • 2
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值