Java8 Optional类的使用

初步认识:

Java 8 中引入了一个很有趣的特性是 Optional 类。Optional 类主要解决的问题是空指针异常(NullPointerException)。这是一个可以为null的容器对象。如果值存在则isPresent()方法会返回true,调用get()方法会返回该对象。

简单示例一:

从前的写法:

public String getName(User user) {
    if (null == user) {
        return "未知用户";
    }
    return user.name;
}

使用了Optional 类的写法:

public String getName(User user) {
    return Optional.ofNullable(user)
        .map(x -> x.name)
        .orElse("未知用户");
}

简单示例二:

从前的写法:

public String getName(King king) throws IllegalArgumentException {
    if (null != king) {
        King result = king.getResult();
        if (null != result) {
            User realKing = result.getKing();
            if (null != realKing) {
                return realKing.getName();
            }
        }
    }
    throw new IllegalArgumentException("本届海贼王尚未诞生!");
}

使用了Optional 类的写法:

public String getName(King king) throws IllegalArgumentException {
    return Optional.ofNullable(king)
        .map(x -> x.getResult())
        .map(y -> y.getKing())
        .map(z -> z.getName())
        .orElseThrow(() -> new IllegalArgumentException("本届海贼王尚未诞生!"));
}

看到这里,相信大家已经对Optional 类有了足够的兴趣,接下来我们就看看它的具体方法及使用。


具体使用:

1、of:

为非null的值创建一个Optional。(如果传入参数为null,则抛出NullPointerException)

@Test
public void testOf() {
	Optional<String> ofOptional = Optional.of("张三");
	Optional<String> nullOptional = Optional.of(null);
}

2、ofNullable:

为指定的值创建一个Optional,如果指定的值为null,则返回一个空的Optional。

@Test
public void testOfNullable() {
    Optional<String> nullOptional = Optional.ofNullable(null);
    System.out.println(nullOptional);

    Optional<String> ofNullableOptional = Optional.ofNullable("李四");
    System.out.println(ofNullableOptional);
}

3、empty:

创建一个空的Optional对象。

@Test
public void testEmpty() {
    Optional<String> emptyOptional = Optional.empty();
    System.out.println(emptyOptional);
}

4、get:

如果Optional对象中有值存在则返回此值。(如果没有值存在,则抛出NoSuchElementException)

@Test
public void testGet() {
    Optional<String> stringOptional = Optional.of("王五");
    System.out.println(stringOptional.get());

    Optional<String> emptyOptional = Optional.empty();
    System.out.println(emptyOptional.get());
}

5、orElse:

如果Optional实例有值则将其返回,否则返回orElse方法传入的参数。

@Test
public void testOrElse() {
    Optional<String> stringOptional = Optional.of("赵六");
    System.out.println(stringOptional.orElse("备用输出(赵六)"));

    Optional<String> emptyOptional = Optional.empty();
    System.out.println(emptyOptional.orElse("orElse(空)"));
}

6、orElseGet:

orElseGet与orElse方法类似,区别在于得到的默认值。orElse方法将传入的字符串作为默认值,orElseGet方法可以接受Supplier接口的实现用来生成默认值。

@Test
public void testOrElseGet() {
    Optional<String> stringOptional = Optional.of("孙七");
    System.out.println(stringOptional.orElseGet(() -> "孙七"));

    Optional<String> emptyOptional = Optional.empty();
    System.out.println(emptyOptional.orElseGet(() -> "orElseGet(空)"));
}

7、orElseThrow:

如果Optional实例有值则将其返回,否则抛出一个由指定的Supplier接口生成的异常。

@Test
public void testOrElseThrow() {
    Optional<String> stringOptional = Optional.of("钱八");
    System.out.println(stringOptional.orElseThrow(CustomException::new));

    Optional<String> emptyOptional = Optional.empty();
    System.out.println(emptyOptional.orElseThrow(CustomException::new));
}
private static class CustomException extends RuntimeException {
    public CustomException() {
        super("自定义异常");
    }
    public CustomException(String message) {
        super(message);
    }
}

8、filter:

如果值存在并且满足断言条件返回包含该值的Optional,否则返回空Optional。

@Test
public void testFilter() {
    Optional<String> stringOptional = Optional.of("biggerthanfive");
    System.out.println(stringOptional.filter(e -> e.length() > 5).orElse("长度大于5"));

    stringOptional = Optional.of("oh");
    System.out.println(stringOptional.filter(e -> e.length() > 5).orElse("长度小于5"));
}

注意:Optional中的filter方法和Stream中的filter方法是有点不一样的。Stream中的filter方法是对一堆元素进行过滤,而Optional中的filter方法只是对一个元素进行过滤,可以把Optional看成是最多只包含一个元素的Stream。

9、map:

如果有值,则对其执行调用mapping函数得到返回值。如果返回值不为null,则创建包含mapping返回值的Optional作为map方法返回值,否则返回空Optional。

@Test
public void testMap() {
    Optional<String> stringOptional = Optional.of("small");
    System.out.println(stringOptional.map(e -> e.toUpperCase()).orElse("大写转换失败"));

    stringOptional = Optional.empty();
    System.out.println(stringOptional.map(e -> e.toUpperCase()).orElse("大写转换失败"));
}

10、flatMap:

flatMap方法与map方法类似,区别在于mapping函数的返回值不同。map方法的mapping函数返回值可以是任何类型T,而flatMap方法的mapping函数必须是Optional。调用结束时,flatMap不会对结果用Optional封装。

@Test
public void testFlatMap() {
    Optional<String> stringOptional = Optional.of("刘九");
    System.out.println(stringOptional.flatMap(e -> Optional.of("卓十")).orElse("转换失败"));

    stringOptional = Optional.of("刘九");
    System.out.println(stringOptional.flatMap(e -> Optional.empty()).orElse("转换失败"));
}

11、ifPresent:

如果Optional实例有值则为其调用Consumer接口,否则不做处理。

@Test
public void testIfPresent() {
    Optional<String> stringOptional = Optional.of("萧十一");
    stringOptional.ifPresent(e -> System.out.println("我被执行了…………" + e));
}

这里补充两段代码比较orElse与orElseGet的不同:

12、值为null时,效果一样:

@Test
public void testdiffOne() {
    String str = null;
    log.info("Using orElse");
    String result1 = Optional.ofNullable(str).orElse(createNewString());
    log.info("Using orElseGet");
    String result2 = Optional.ofNullable(str).orElseGet(() -> createNewString());
}
private String createNewString() {
    log.info("Creating New String");
    return new String("新·字符串");
}

13、值不为null时,使用orElseGet不会调用createNewString方法:

@Test
public void testdiffTwo() {
    String str = "不为null";
    log.info("Using orElse");
    String result1 = Optional.ofNullable(str).orElse(createNewString());
    log.info("Using orElseGet");
    String result2 = Optional.ofNullable(str).orElseGet(() -> createNewString());
}
private String createNewString() {
    log.info("Creating New String");
    return new String("新·字符串");
}

一句话小结:使用 Optional 时尽量不直接调用 Optional.get() 方法,Optional.isPresent() 更应该被视为一个私有方法,应依赖于其他像 Optional.orElse(),Optional.orElseGet(),Optional.map() 等这样的方法。

最后,Java 9 为 Optional 类添加了三个方法:or()、ifPresentOrElse() 和 stream()。有兴趣的朋友可以自行继续拓展,此处只做引出不再深入。


版权声明:本文为NarutoConanKing原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接和本声明。