Lambda Optional使用

2023-10-20 12:35:46 浏览数 (2)

简述

Optional 针对空指处理而设计的类型。 Java 8借鉴了ScalaHaskell,提供了一个新的Optional模板,可以用它来封装可能为空的引用。这是一个可以为null的容器对象。 使用 Optional 的好处是可以以一种专门针对null的处理方式,来避免值可能存在 null 导致出现的程序异常。你可以理解为:处理null,就是你的业务。 在实际使用过程中,你会发现 Optional 的灵活性有时候会让你想用在任何可能出现null的地方,不过凡事都有套路可循,只要清楚利弊就知道该如何选择。

从两个方面说一下 Optional

  • 常用API
  • 项目使用套路

只讲 API 不讲使用套路的文章都是耍流氓。

API说明

先看一下常用API,后面再讲实际使用场景。

  1. 构建API: 构建Optional对象:of()、ofNullable()、empty()
  2. 获取API: 获取Optional对象包装的值:get()、orElse()、orElseGet()、orElseThrow()
  3. 判断API:对Optional对象里包装的值做一些逻辑判断:isPresent()、ifPresent()、filter()
  4. 转换API:将Optional对象里包装的值转换成一个新的值:map()、flatMap();

构建API

Optional.of()

作用:构建 Optional 对象,不允许传入的值为null,传入就 null 马上抛异常。

这个API要慎用,一般在使用 Optional 时,就是要防住 null,这个API 上来就直接抛异常,一点机会也不给。

代码语言:javascript复制
public static void testOf() {
    Optional<String> op1 = Optional.of("Hello World");
    System.out.println(op1.isPresent()); // 输出 true
    System.out.println(op1.get()); // 输出 Hello
    Optional<String> op2 = Optional.of(null); // 抛出异常
}

Optional.ofNullable()

常用API。

允许传入的值为 null,如果值为 null,返回一个空的 Optional 传入 null 并不抛异常。

使用 Optional.get() 获取值时,有值正常返回,值为 null 抛异常。

代码语言:javascript复制
public static void testOfNullable() {
    //传入不报错
    Optional<String> name = Optional.ofNullable(null);
    System.out.println(name);      //直接输出是 Optional.empty
    System.out.println(name.isPresent()); //判断是否有值
    System.out.println(name.get()); //  get()  抛异常
}

Optional.empty()

作用:创建一个空的 Optional 对象,一般很少直接这样写,都是通过 ofNullable 直接接住变量。

empty()方法创建的对象没有值,如果对 emptyOpt 变量调用isPresent()方法会返回false, 调用get()方法抛出NullPointerException异常。

代码语言:javascript复制
public static void testEmpty() {
    Optional<String> emptyOpt = Optional.empty();
    System.out.println(emptyOpt.isPresent()); // 输出 false
    System.out.println(emptyOpt.get()); // 抛异常
}

获取API

这一组API很常用:get、orElse、orElseGet、orElseThrow。 get() 使用简单,后面三个简单一些业务逻辑。

Optional.get()

作用:获取 Optional 中的数据。

可以看上一个例子。使用 Optional 时,如查值是 null,get 会抛异常。

orElse()

作用:如果有值就返回不执行,否则如果值为null,也会执行orElse();

这种做用是相当于在特定场景下的用法可以用它来代替if..else..来完成很简洁的逻辑判断。 看到 orElse 中只有一个String不能做别的事?当然不是,可以写一个方法,orElse调用该方法,就可以写其他代码。

代码语言:javascript复制
import java.util.Optional;

public class OptionalTest {
    public static void main(String[] args){
        testOrElse(null);
    }

    public static void testOrElse(String nullValue) {
        // 入参为 null,执行 orElse
        String optional = Optional.ofNullable(nullValue).orElse("Su");
        System.out.println(optional);
        // 入参为 Susan,不执行 orElse
        String nonNullOptional = Optional.ofNullable("Susan").orElse("Su");
        System.out.println(nonNullOptional);
    }
}

结果

Su Susan

orElseGet()

作用:入参为null时,才会执行 orElseGet()。

和orElse的区别: 在optional为空值的情况下orElse和orElseGet都会执行,当optional不为空时,orElseGet不会执行。

代码语言:javascript复制
import java.util.Optional;

public class OptionalTest {
    public static void main(String[] args){
        testOrElseGet(null);
    }

    public static void testOrElseGet(String nullValue) {
        String optionalGet = Optional.ofNullable(nullValue).orElseGet(() -> "Xiao");
        System.out.println(optionalGet);

        String nonNullOptionalGet = Optional.ofNullable("Molly").orElseGet(() -> "Xiao");
        System.out.println(nonNullOptionalGet);
    }
}

结果

Xiao Molly

orElseThrow()

作有:当参数为空时,抛异常。

代码语言:javascript复制
import java.util.Optional;

public class OptionalTest {
    public static void main(String[] args){
        testOrElseThrow(null);
    }

    public static void testOrElseThrow(String nullValue) {
        try {
            Optional.ofNullable(nullValue)
                .orElseThrow(()-> new Exception("参数为空"));
        } catch (Exception e) {
        e.printStackTrace();
        }
    }
}

结果

java.lang.Exception: 参数为空 at com.test.OptionalTest.lambdatestOrElseThrow2(OptionalTest.java:30) at java.util.Optional.orElseThrow(Optional.java:290) at com.test.OptionalTest.testOrElseThrow(OptionalTest.java:30) at com.test.OptionalTest.main(OptionalTest.java:9)

判断API

Optional.isPresent()

作用:判断变量是否为null

这个比较常用,用来提前判断一下值是否为空。实际业务场景中,很参数传入的时候,程序是不知道是否为null的,可以使用这个先进行判断。

代码语言:javascript复制
public static void testIsPersent() {
    //传入不报错
    Optional<String> name = Optional.ofNullable(null);
    System.out.println(name.isPresent()); //判断是否有值: true; false
    if (name.isPresent()) {
        System.out.println("有值");
    } else {
        System.out.println("空值");
    }
}

Optional.ifPresent()

很常有的API。

作用:如果值不为null,则可以执行后续操作。

看这段代码,如果值为null不会进入 ifPresent,如果不为null,则进入。这样就不需要if来判断这种特性很方便写一个流畅的工作流风格代码。

代码语言:javascript复制
public static void testIfPersent() {
    String value = "test";
    Optional.ofNullable(value)  
    .ifPresent((s) -> {
        System.out.println(s);
    }
}

Optional.filter()

作用:条件过滤,根据条件过滤不满足条件的数据。

代码语言:javascript复制
public List<UserInfo> getUsers() {
    List<UserInfo> userInfo = userService.getUsers();
    List<UserInfo> newUserInfo = Optional
                          .ofNullable(userInfo)
                          .filter(u -> u.getPrice() < 1000)
                          .orElse(UserInfo.builder()
                          .build());
    //直接 return,orElse处理了null,userInfo不会为null
    return userInfo;
}

Optional.map()

作用:映射出新对象。map 时return 什么类型的数据,接收时就必须使用对应的泛型接住。

代码语言:javascript复制
public static void testMap() {
    String val1 = "test";
    String val2 = null;
    Optional<String> optional = Optional.ofNullable(val2);
    Optional<String> newVal = optional.map((a) -> {
        System.out.println(a);
        return "bb";
    });
    System.out.println(newVal.get());
}

结果: 入参为 val1 时

bb

入参为 val2 时

Exception in thread "main" java.util.NoSuchElementException: No value present

套路

使用 Optional 时,Optional.get() 如果值为 null,还是会抛异常,那使用 Optional 有什么意义。 Optional 能不能当作返回出参,返回给外部调用。

先说第一个问题,意义在于,Optional 本身不会为 null,不会在被调用时出现空指针而导致异常。由于是通过 Optional 包裹可能出现空值的对象,所以多了一层保护机制。

Optional 不建议做为返回值,至于为什么后面说。

套路1 不返回null

保证返回的数据中绝对不返回null,保证不会因为null引起不可预见的异常。 结合 orElse,来保证如果下面的 list 中查出的数据是null,就返回一个空的ArrayList。 这种写法简单实用。

代码语言:javascript复制
public List<User> getUsers() {
    List<User> userInfo = userService.getUsers();
    return Optional
            .ofNullable(userInfo)
            .orElse(new ArrayList());

}

套路2 先判断,后使用

业务中从一个Service中获得一个数据,那么先处理一下。

判断List是否为空

代码语言:javascript复制
public static void testService() {
    List<User> users = userService.getUsers();
    Optional<String> usersOptional = Optional.ofNullable(users);
    // 这里用 Optional 接住,再进行判断
    if (usersOptional.isPresent()) {
        //do somthine
    }
}

判断List是否为空,不为空执行后续

代码语言:javascript复制
public static void testService() {
    List<User> users = userService.getUsers();
    Optional<String> usersOptional = Optional.ofNullable(users);
    usersOptional.ifPresent(user -> {
        System.out.println(user.getSize());
    })
}

这两种写法,其实很相近,该怎么选择呢,简化一下代码,如果只有在有值的情况下才处理,使用 ifPresent 的处理,是最简洁的。

代码语言:javascript复制
public void test() {
  //正解
  Optional<User> userOpt = Optional.ofNullable(user);
  userOpt.ifPresent(System.out.println(user.get()));

  //而非
  if (userOpt.isPresent()) {
    System.out.println(user.get());
  }
}

这两种方式都是用在处理没程不需要返回值的情况下。

套路3 结合 Stream 使用

开发中使用Stream应该是用的最多的,还是一样的套路,防止出现 List 为空。

代码语言:javascript复制
List<Person> personList = personService.getPersons();
Optional.ofNullable(personList)
        .orElseGet(() -> {
            System.out.println("personList为null!");
            return new ArrayList<>();
        })
        .stream()
        .filter(Objects::nonNull)
        .forEach(person -> {
            System.out.println(person.getName());
            System.out.println(person.getAge());
        });

总结

Optional 的功能主要是在保证参数不出现null,通过提供的API来实现,让代码更加健壮。 健壮的代码有助于提高系统的稳定性,是一种不可多得的处理手段。即使不使用 Optional,也需要保证,不直接将 null 返回给上一级调用在方法内处理掉null。 不能相信调用的方法是安全的,需要自己对null有安全的处理。 Optional 只是简化了null的操作,即使没有 Optional 也要对null的处理放在一个重点关注的位置。

0 人点赞