Java8 的 Optional 类使用方法

什么是 Optional 类?

Optional 类 (java.util.Optional) 是一个容器类,代表一个值存在或不存在,原来用 null 表示一个值不存在,现在 Optional 可以更好的表达这个概念。并且可以避免空指针异常。

Optional 类常用方法:


Optional.of(T t) : 创建一个 Optional 实例。

Optional.empty() : 创建一个空的 Optional 实例。

Optional.ofNullable(T t):若 t 不为 null,创建 Optional 实例,否则创建空实例。

isPresent() : 判断是否包含值。

orElse(T t) : 如果调用对象包含值,返回该值,否则返回t。

orElseGet(Supplier s) :如果调用对象包含值,返回该值,否则返回 s 获取的值。

map(Function f): 如果有值对其处理,并返回处理后的Optional,否则返回 Optional.empty()。

flatMap(Function mapper):与 map 类似,要求返回值必须是Optional。

Optional 类示例

1. 创建 Optional 类
(1)使用 empty() 方法创建一个空的 Optional 对象:

Optional<String> empty = Optional.empty();

(2)使用 of() 方法创建 Optional 对象:

String name = "binghe";
Optional<String> opt = Optional.of(name);
assertEquals("Optional[binghe]", opt.toString());

传递给 of() 的值不可以为空,否则会抛出空指针异常。例如,下面的程序会抛出空指针异常。

String name = null;
Optional<String> opt = Optional.of(name);

如果我们需要传递一些空值,那我们可以使用下面的示例所示。

String name = null;
Optional<String> opt = Optional.ofNullable(name);

使用 ofNullable()方法,则当传递进去一个空值时,不会抛出异常,而只是返回一个空的 Optional 对象,如同我们用 Optional.empty() 方法一样。

.isPresent

我们可以使用这个 isPresent() 方法检查一个 Optional 对象中是否有值,只有值非空才返回 true。

Optional<String> opt = Optional.of("binghe");
assertTrue(opt.isPresent());

opt = Optional.ofNullable(null);
assertFalse(opt.isPresent());

在 Java8 之前,我们一般使用如下方式来检查空值。

if(name != null){
    System.out.println(name.length);
}

在 Java8 中,我们就可以使用如下方式来检查空值了。

Optional<String> opt = Optional.of("binghe");
opt.ifPresent(name -> System.out.println(name.length()));

3.orElse 和 orElseGet

(1)orElse

orElse() 方法用来返回 Optional 对象中的默认值,它被传入一个“默认参数‘。如果对象中存在一个值,则返回它,否则返回传入的“默认参数”。

String nullName = null;
String name = Optional.ofNullable(nullName).orElse("binghe");
assertEquals("binghe", name);

(2)orElseGet

与 orElse() 方法类似,但是这个函数不接收一个“默认参数”,而是一个函数接口。

String nullName = null;
String name = Optional.ofNullable(nullName).orElseGet(() -> "binghe");
assertEquals("binghe", name);

(3)二者有什么区别?

要想理解二者的区别,首先让我们创建一个无参且返回定值的方法。

public String getDefaultName() {
    System.out.println("Getting Default Name");
    return "binghe";
}

接下来,进行两个测试看看两个方法到底有什么区别。

String text;
System.out.println("Using orElseGet:");
String defaultText = Optional.ofNullable(text).orElseGet(this::getDefaultName);
assertEquals("binghe", defaultText);

System.out.println("Using orElse:");
defaultText = Optional.ofNullable(text).orElse(getDefaultName());
assertEquals("binghe", defaultText);

在这里示例中,我们的 Optional 对象中包含的都是一个空值,让我们看看程序执行结果:

Using orElseGet:
Getting default name...
Using orElse:
Getting default name...

两个 Optional 对象中都不存在 value,因此执行结果相同。

那么,当 Optional 对象中存在数据会发生什么呢?我们一起来验证下。

String name = "binghe001";

System.out.println("Using orElseGet:");
String defaultName = Optional.ofNullable(name).orElseGet(this::getDefaultName);
assertEquals("binghe001", defaultName);

System.out.println("Using orElse:");
defaultName = Optional.ofNullable(name).orElse(getDefaultName());
assertEquals("binghe001", defaultName);

运行结果如下所示。

Using orElseGet:
Using orElse:
Getting default name...

可以看到,当使用 orElseGet()方法时,getDefaultName() 方法并不执行,因为 Optional 中含有值,而使用 orElse 时则照常执行。所以可以看到,当值存在时,orElse 相比于 orElseGet,多创建了一个对象。如果创建对象时,存在网络交互,那系统资源的开销就比较大了,这是需要我们注意的一个地方。

4.orElseThrow

orElseThrow() 方法当遇到一个不存在的值的时候,并不返回一个默认值,而是抛出异常。

String nullName = null;
String name = Optional.ofNullable(nullName).orElseThrow( IllegalArgumentException::new);

5.get

get() 方法表示是 Optional 对象中获取值。

Optional<String> opt = Optional.of("binghe");
String name = opt.get();
assertEquals("binghe", name);

使用 get() 方法也可以返回被包裹着的值。但是值必须存在。当值不存在时,会抛出一个 NoSuchElementException 异常。

Optional<String> opt = Optional.ofNullable(null);
String name = opt.get();

6.filter

接收一个函数式接口,当符合接口时,则返回一个 Optional 对象,否则返回一个空的 Optional 对象。

String name = "binghe";
Optional<String> nameOptional = Optional.of(name);
boolean isBinghe = nameOptional.filter(n -> "binghe".equals(name)).isPresent();
assertTrue(isBinghe);
boolean isBinghe001 = nameOptional.filter(n -> "binghe001".equals(name)).isPresent();
assertFalse(isBinghe001);

使用 filter() 方法会过滤掉我们不需要的元素。

接下来,我们再来看一例示例,例如目前有一个 Person 类,如下所示。

public class Person{
    private int age;
    public Person(int age){
        this.age = age;
    }
    //省略get set方法
}

例如,我们需要过滤出年龄在 25 岁到 35 岁之前的人群,那在 Java8 之前我们需要创建一个如下的方法来检测每个人的年龄范围是否在 25 岁到 35 岁之前。

public boolean filterPerson(Peron person){
    boolean isInRange = false;
    if(person != null && person.getAge() >= 25 && person.getAge() <= 35){
        isInRange =  true;
    }
    return isInRange;
}

看上去就挺麻烦的,我们可以使用如下的方式进行测试。

assertTrue(filterPerson(new Peron(18)));
assertFalse(filterPerson(new Peron(29)));
assertFalse(filterPerson(new Peron(16)));
assertFalse(filterPerson(new Peron(34)));
assertFalse(filterPerson(null));

如果使用 Optional,效果如何呢?

public boolean filterPersonByOptional(Peron person){
     return Optional.ofNullable(person)
       .map(Peron::getAge)
       .filter(p -> p >= 25)
       .filter(p -> p <= 35)
       .isPresent();
}

使用 Optional 看上去就清爽多了,这里,map() 仅仅是将一个值转换为另一个值,并且这个操作并不会改变原来的值。

7.map

如果有值对其处理,并返回处理后的 Optional,否则返回 Optional.empty()。

List<String> names = Arrays.asList("binghe001", "binghe002", "", "binghe003", "", "binghe004");
Optional<List<String>> listOptional = Optional.of(names);

int size = listOptional
    .map(List::size)
    .orElse(0);
assertEquals(6, size);

在这个例子中,我们使用一个 List 集合封装了一些字符串,然后再把这个 List 使用 Optional 封装起来,对其 map(),获取 List 集合的长度。map() 返回的结果也被封装在一个 Optional 对象中,这里当值不存在的时候,我们会默认返回 0。如下我们获取一个字符串的长度。

String name = "binghe";
Optional<String> nameOptional = Optional.of(name);

int len = nameOptional
    .map(String::length())
    .orElse(0);
assertEquals(6, len);

我们也可以将 map()方法与 filter() 方法结合使用,如下所示。

String password = " password ";
Optional<String> passOpt = Optional.of(password);
boolean correctPassword = passOpt.filter(
    pass -> pass.equals("password")).isPresent();
assertFalse(correctPassword);

correctPassword = passOpt
    .map(String::trim)
    .filter(pass -> pass.equals("password"))
    .isPresent();
assertTrue(correctPassword);

上述代码的含义就是对密码进行验证,查看密码是否为指定的值。

8.flatMap

与 map 类似,要求返回值必须是 Optional。

假设我们现在有一个 Person 类。

public class Person {
    private String name;
    private int age;
    private String password;
 
    public Optional<String> getName() {
        return Optional.ofNullable(name);
    }
 
    public Optional<Integer> getAge() {
        return Optional.ofNullable(age);
    }
 
    public Optional<String> getPassword() {
        return Optional.ofNullable(password);
    }
    // 忽略get set方法
}

接下来,我们可以将 Person 封装到 Optional 中,并进行测试,如下所示。

Person person = new Person("binghe", 18);
Optional<Person> personOptional = Optional.of(person);

Optional<Optional<String>> nameOptionalWrapper = personOptional.map(Person::getName);
Optional<String> nameOptional = nameOptionalWrapper.orElseThrow(IllegalArgumentException::new);
String name1 = nameOptional.orElse("");
assertEquals("binghe", name1);

String name = personOptional
    .flatMap(Person::getName)
    .orElse("");
assertEquals("binghe", name);

注意:方法 getName 返回的是一个 Optional 对象,如果使用 map,我们还需要再调用一次 get()方法,而使用 flatMap() 就不需要了。
————————————————
版权声明:本文为 CSDN 博主「冰 河」的原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接及本声明。
原文链接:https://blog.csdn.net/l1028386804/article/details/106447083

  
    展开阅读全文