方法 | 描述 |
of | 把指定的值封装为Optional对象,如果指定的值为null,则抛出NullPointerException |
empty | 创建一个空的Optional对象 |
ofNullable | 把指定的值封装为Optional对象,如果指定的值为null,则创建一个空的Optional对象 |
get | 如果创建的Optional中有值存在,则返回此值,否则抛出NoSuchElementException |
orElse | 如果创建的Optional中有值存在,则返回此值,否则返回一个默认值 |
orElseGet | 如果创建的Optional中有值存在,则返回此值,否则返回一个由Supplier接口生成的值 |
orElseThrow | 如果创建的Optional中有值存在,则返回此值,否则抛出一个由指定的Supplier接口生成的异常 |
filter | 如果创建的Optional中的值满足filter中的条件,则返回包含该值的Optional对象,否则返回一个空的Optional对象 |
map | 如果创建的Optional中的值存在,对该值执行提供的Function函数调用 |
flagMap | 如果创建的Optional中的值存在,就对该值执行提供的Function函数调用,返回一个Optional类型的值,否则就返回一个空的Optional对象 |
isPresent | 如果创建的Optional中的值存在,返回true,否则返回false |
ifPresent | 如果创建的Optional中的值存在,则执行该方法的调用,否则什么也不做 |
用法:of,ofNullable,get,empty
//##### of #####
User user=new User("1","张三");
Optional<User> opt = Optional.of(user);
System.out.println(opt.get().getId());
结果:opt: 1
Optional<User> opt = Optional.of(null);//创建空的Optional会报空指针错误
结果:opt: java.lang.NullPointerException
//##### ofNullable #####
Optional<String> opt = Optional.ofNullable(null);//创建空的Optional也不会报错
Optional<String> opt2 = Optional.ofNullable("123");
//##### get #####
//如果我们创建的Optional对象中有值存在则返回此值,
//如果没有值存在,则会抛出 NoSuchElementException异常
Optional<String> opt = Optional.of("123");
System.out.println(opt.get());
//##### empty #####
Optional<String> opt = Optional.empty();//创建一个空的String类型的Optional对象
System.out.println(opt.get());
结果 : 访问 emptyOpt 变量的值会导致 NoSuchElementException
orElse,orElseGet
//##### orElse ##### 它的工作方式非常直接,如果有值则返回该值,否则返回传递给它的参数值
Optional<String> stringOptional = Optional.of("1");
System.out.println(stringOptional.orElse("2"));
结果:1
Optional<String> emptyOptional = Optional.empty();
System.out.println(emptyOptional.orElse("2"));
结果:2
//##### orElseGet #####
//其行为略有不同。这个方法会在有值的时候返回值,如果没有值,它会执行作为参数传入的
//Supplier(供应者) 函数式接口,并将返回其执行结果
Optional<String> stringOptional = Optional.of("1");
System.out.println(stringOptional.orElseGet(() -> "2"));
结果:1
Optional<String> emptyOptional = Optional.empty();
System.out.println(emptyOptional.orElseGet(() -> "2"));
结果:2
orElse() 和 orElseGet() 的不同之处
乍一看,这两种方法似乎起着同样的作用。然而事实并非如此。我们创建一些示例来突出二者行为上的异同。
我们先来看看对象为空时他们的行为:
@Test
public void givenEmptyValue_whenCompare_thenOk() {
User user = null
logger.debug("Using orElse");
User result = Optional.ofNullable(user).orElse(createNewUser());
logger.debug("Using orElseGet");
User result2 = Optional.ofNullable(user).orElseGet(() -> createNewUser());
}
private User createNewUser() {
logger.debug("Creating New User");
return new User("extra@gmail.com", "1234");
}
上面的代码中,两种方法都调用了 createNewUser() 方法,这个方法会记录一个消息并返回 User 对象。
代码输出如下:
Using orElse
Creating New User
Using orElseGet
Creating New User
由此可见,当对象为空而返回默认对象时,行为并无差异。
我们接下来看一个类似的示例,但这里 Optional 不为空:
@Test
public void givenPresentValue_whenCompare_thenOk() {
User user = new User("john@gmail.com", "1234");
logger.info("Using orElse");
User result = Optional.ofNullable(user).orElse(createNewUser());
logger.info("Using orElseGet");
User result2 = Optional.ofNullable(user).orElseGet(() -> createNewUser());
}
private User createNewUser() {
logger.debug("Creating New User");
return new User("extra@gmail.com", "1234");
}
这次的输出:
Using orElse
Creating New User
Using orElseGet
这个示例中,两个 Optional 对象都包含非空值,两个方法都会返回对应的非空值。不过,orElse() 方法仍然创建了 User 对象。与之相反,orElseGet() 方法不创建 User 对象。
在执行较密集的调用时,比如调用 Web 服务或数据查询,这个差异会对性能产生重大影响。
orElseThrow
//##### orElseThrow #####
@Test
public void OptionalTest() {
User user=null;
User result = Optional.ofNullable(user)
.orElseThrow( () -> new IllegalArgumentException());
}
结果:java.lang.IllegalArgumentException
这里,如果 user 值为 null,会抛出 IllegalArgumentException。
这个方法让我们有更丰富的语义,可以决定抛出什么样的异常,而不总是抛出 NullPointerException。
现在我们已经很好地理解了如何使用 Optional,我们来看看其它可以对 Optional 值进行转换和过滤的方法。
转换值:map
对stringOptional的值进行操作
@Test
public void OptionalTest() {
Optional<String> stringOptional = Optional.of("zhangsan");
System.out.println(stringOptional.map(e -> e.toUpperCase()).orElse("失败"));
stringOptional = Optional.empty();
System.out.println(stringOptional.map(e -> e.toUpperCase()).orElse("失败"));
}
结果:
ZHANGSAN
失败
flagMap
Optional<String> stringOptional = Optional.of("zhangsan");
System.out.println(stringOptional.flatMap(e -> Optional.of("lisi")).orElse("失败"));
stringOptional = Optional.empty();
System.out.println(stringOptional.flatMap(e -> Optional.empty()).orElse("失败"));
结果:
lisi
失败
map 和flagMap 的区别
map方法中的lambda表达式返回值可以是任意类型,在map函数返回之前会包装为Optional。
但flatMap方法中的lambda表达式返回值必须是Optionl实例
filter
如果创建的Optional中的值满足filter中的过滤条件,则返回包含该值的Optional对象
否则返回一个空的Optional对象
@Test
public void OptionalTest() {
Optional<String> stringOptional = Optional.of("12345");
System.out.println(stringOptional.filter(e -> e.length() >= 5).orElse("flase"));
stringOptional = Optional.empty();//清空
System.out.println(stringOptional.filter(e -> e.length() >= 5).orElse("flase"));
}
结果:
12345
false
ifPresent
ifPresent方法的参数是一个Consumer的实现类,Consumer类包含一个抽象方法,该抽象方法对传入的值进行处理,
只处理没有返回值。
@Test
public void OptionalTest() {
Optional<String> stringOptional = Optional.of("zhangsan");
stringOptional.ifPresent(e-> System.out.println("我被处理了。。。"+e));
}
结果
我被处理了。。。zhangsan
Optional 应该怎样用?
在使用 Optional 的时候需要考虑一些事情,以决定什么时候怎样使用它。
重要的一点是 Optional 不是 Serializable。因此,它不应该用作类的字段。
如果你需要序列化的对象包含 Optional 值,Jackson 库支持把 Optional 当作普通对象。也就是说,Jackson 会把空对象看作 null,而有值的对象则把其值看作对应域的值。这个功能在 jackson-modules-java8 项目中。
Optional 主要用作返回类型。在获取到这个类型的实例后,如果它有值,你可以取得这个值,否则可以进行一些替代行为。
Optional 类有一个非常有用的用例,就是将其与流或其它返回 Optional 的方法结合,以构建流畅的API。
我们来看一个示例,使用 Stream 返回 Optional 对象的 findFirst() 方法
@Test
public void OptionalTest() {
List<User> users = new ArrayList<>();
users.add(new User("1","name"));
User user = users.stream().findFirst().orElse(new User("default", "default"));
System.out.println(user.getId()+" "+user.getName());
}
结果:1 name
@Test
public void OptionalTest() {
List<User> users = new ArrayList<>();
User user = users.stream().findFirst().orElse(new User("default", "default"));
System.out.println(user.getId()+" "+user.getName());
}
结果:default default
实例:
//优化前
public static String getChampionName(Competition comp) throws IllegalArgumentException {
if (comp != null) {
CompResult result = comp.getResult();
if (result != null) {
User champion = result.getChampion();
if (champion != null) {
return champion.getName();
}
}
}
throw new IllegalArgumentException("The value of param comp isn't available.");
}
//优化后
public static String getChampionName(Competition comp) throws IllegalArgumentException {
return Optional.ofNullable(comp)
.map(c->c.getResult())
.map(r->r.getChampion())
.map(u->u.getName())
.orElseThrow(()->new IllegalArgumentException("The value of param comp isn't available."));
}
总结
Optional 是 Java 语言的有益补充 —— 它旨在减少代码中的 NullPointerExceptions,虽然还不能完全消除这些异常。
它也是精心设计,自然融入 Java 8 函数式支持的功能。
总的来说,这个简单而强大的类有助于创建简单、可读性更强、比对应程序错误更少的程序。