理解、学习JAVA中的Optional

理解、学习JAVA中的Optional

标签: Java8


思考: 调用一个方法得到了返回值却不能直接将返回值作为参数去调用别的方法。

原来解决方案: 我们首先要判断这个返回值是否为null,只有在非空的前提下才能将其作为其他方法的参数。这正是一些类似Guava的外部API试图解决的问题。一些JVM编程语言比如Scala、Ceylon等已经将对在核心API中解决了这个问题。

新版本的Java,比如Java8引入了一个新的Optional类。。Optional类主要解决的问题是臭名昭著空指针异常(NullPointerException)—-每个程序员都非常了解的异常。Optional类的Javadoc描述如下:

这是一个可以为null的容器对象。如果值存在则isPresent()方法会返回true,调用get()方法会返回该对象。

本质上,这是一个包含有可选值的包装类,则意味着Optional类既可以含有对象也可以为空。

Optional是Java实现函数式编程的强劲一步,并且帮助在范式中实现。但是Optional的意义显然不止于此。

我们从一个简单的用例开始。在Java8之前,任何访问对象方法或属性的调用都有可能导致NullPointerException。

String iscode=user.getAddress().getCountry().getIsoCode().toUpperCase();

在这个小示例中,如果我们需要确保不触发异常,就得在访问每一个值之前对其进行明确地检查。

if (user!=null){
    Address address=user.getAddress();
    if (address!=null){
        Country country=address.getCountry();
        if (country!=null){
            String isocode=country.getIsocode();
            if (isocode!=null){
                isocode = isocode.toUpperCase();
            }
        }
    }
}

你看到了,这很容易变得冗长,难以维护。
为了简化这个过程,我们来看看Optional类是怎么做的。从创建和验证实例,到使用其不同的方法,并与其它返回相同类型的方法相结合。

创建Optional实例

这个类型的对象可能包含空值,也可能为空。你可以使用同名方法创建一个空的 Optional。

Optional name = Optional.empty();

毫不奇怪,尝试访问 name 变量的值会导致 NoSuchElementException。

你可以使用of()和ofNullable()方法创建包含值的Optional。
两个方法的不同之处在于如果你把 null 值作为参数传递进去,of() 方法会抛出 NullPointerException。

of():为非null的值创建一个Optional

//调用工厂方法创建Optional实例
Optional name = Optional.of("Optional");
//传入参数为null,抛出NullPointerException.
Optional someNull = Optional.of(null);

你看,我们并没有完全摆脱 NullPointerException。因此,你应该明确对象不为 null 的时候使用 of()。

如果对象即可能是 null 也可能是非 null,你就应该使用 ofNullable() 方法:

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

//下面创建了一个不包含任何值的Optional实例
//例如,值为'null'
Optional empty = Optional.ofNullable(null);

访问Oprional对象的值

从Oprional实例中取回实际值对象的方法之一是使用get()方法:

get():如果Optional有值则将其返回,否则抛出NoSuchElementException。

//执行下面的代码会输出:No value present 
try {
  //在空的Optional实例上调用get(),抛出NoSuchElementException
  System.out.println(empty.get());
} catch (NoSuchElementException ex) {
  System.out.println(ex.getMessage());
}

不过,你看到了,这个方法会在值为null的时候抛出异常。要避免异常,你可以选择首先验证是否有值:

isPresent():如果值存在返回true,否则返回false。

Optional name = Optional.of("Optional");
//isPresent方法用来检查Optional实例中是否包含值
if (name.isPresent()) {
  //在Optional实例内调用get()返回已存在的值
  System.out.println(name.get());//输出Optional
}

检查是否有值的另一个选择是ifPresent()方法。该方法除了执行检查,还接受一个Consumer(消费者) 参数,Consumer类包含一个抽象方法。该抽象方法对传入的值进行处理,但没有返回值。如果对象不是空的,就对执行传入的 Lambda 表达式:

返回默认值

ifPresent():如果Optional实例有值则为其调用consumer,否则不做处理。

Optional name = Optional.of("Optional");
//ifPresent方法接受lambda表达式作为参数。
//lambda表达式对Optional的值调用consumer进行处理。
name.ifPresent((value) -> {
  System.out.println("The length of the value is: " + value.length());
});
//通过ifPresent修改的值,再次通过get获取的时候不会改变

orElse():如果有值将其返回,否则返回指定的其他值。

//如果值不为null,orElse方法返回Optional实例的值。
//如果为null,返回传入的消息。
String orElse = "There is no value present!";
Optional empty = Optional.ofNullable(null);
//输出:There is no value present!
System.out.println(empty.orElse(orElse));
Optional name = Optional.ofNullable("Optional");
//输出:Optional
System.out.println(name.orElse(orElse));

orElseGet():与orElse()方法类似,orElseGet()方法可以接受Supplier接口的实现用来生成默认值。

//orElseGet与orElse方法类似,区别在于orElse传入的是默认值,
//orElseGet可以接受一个lambda表达式生成默认值。
Optional empty = Optional.ofNullable(null
//输出:Default Value
System.out.println(empty.orElseGet(() -> "Default Value"));
Optional name = Optional.ofNullable("Optional");
//输出:Optional
System.out.println(name.orElseGet(() -> "Default Value"));

orElse()和orElseGet()的不同之处

这两种方法似乎起着同样的作用,然而事实并非如此,我们创建一些示例来突出二者行为上的异同。

public static String newDefaultValue(){
    System.out.println("new");
    return new String("Default Value");
}

对象为空时的行为:

Optional empty = Optional.ofNullable(null);
//输出:new Default Value
System.out.println(empty.orElse(newDefaultValue()));
Optional name = Optional.ofNullable(null);
//输出:new Default Value
System.out.println(name.orElseGet(() -> newDefaultValue()));
//当对象为空返回默认值时并无差异

对象不为空时:

Optional empty = Optional.ofNullable("Optioanl");
//输出:new Optioanl
System.out.println(empty.orElse(newDefaultValue()));
Optional name = Optional.ofNullable("Optioanl");
//输出:Optioanl
System.out.println(name.orElseGet(() -> newDefaultValue()));
System.out.println(Boolean.parseBoolean(null));

当两个Optional对象都包含非空值,两个方法都会返回相对应的非空值,不过orElse()方法依然创建了对象,orElseGet()则不创建对象。在进行密集的调用时,这个差异会对性能产生重大影响。

返回异常

orElseThrow():如果有值将其返回否则抛出supplier接口创建的异常。

//orElseThrow与orElse方法类似。与返回默认值不同
//orElseThrow会抛出lambda表达式或方法生成的异常 
try {
  empty.orElseThrow(ValueAbsentException::new);
} catch (Throwable ex) {
  //输出: No value present in the Optional instance
  System.out.println(ex.getMessage());
}

转换值

有很多种方法可以转换 Optional 的值。我们从 map() 和 flatMap() 方法开始。

map():对值调用作为参数的函数,然后将返回的值包装在Optional中。如果返回值不为null,则创建包含mapping返回值的Optional作为map方法返回值,否则返回空Optional。

Optional empty = Optional.empty();
Optional optional = empty.map(String::toUpperCase);
//输出:No value found
System.out.println(optional.orElse("No value found"));
Optional name = Optional.ofNullable("Optional");
Optional optionalS = name.map(String::toUpperCase); 
//输出:OPTIONAL
System.out.println(optionalS.orElse("No value found"));

flatMap():与map()类似,区别在于flatMap中的mapper返回值必须是Optional。调用结束时,flatMap不会对结果用Optional封装。

Optional empty = Optional.empty();
Optional optional = empty.flatMap(s -> Optional.of(s.toUpperCase()));
//输出:No value found
System.out.println(optional.orElse("No value found"));
Optional name = Optional.ofNullable("Optional");
Optional optionalS = name.flatMap(s -> Optional.of(s.toUpperCase()));
//输出:OPTIONAL
System.out.println(optionalS.orElse("No value found"));

过滤值

filter():接受一个Predicate参数,返回包含为true的值的Optional。如果测试结果为 false,会返回一个空的 Optional。

Optional name = Optional.ofNullable("Optional");
Optional longName = name.filter((value) -> value.length() > 6);
//输出Optional
System.out.println(longName.orElse("The name is less than 6 characters"));
//另一个例子是Optional值不满足filter指定的条件。
Optional anotherName = Optional.of("test");
Optional shortName = anotherName.filter((value) -> value.length() > 6);
//输出:The name is less than 6 characters
System.out.println(shortName.orElse("The name is less than 6 characters"));

关注下面微信公众号获得更多学习资源!
理解、学习JAVA中的Optional_第1张图片

你可能感兴趣的:(java)