Java8中的新特性Optional
Posted lina2014
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Java8中的新特性Optional相关的知识,希望对你有一定的参考价值。
Optional 类是一个可以为null的容器对象。如果值存在则isPresent()方法会返回true,调用get()方法会返回该对象。
Optional 是个容器:它可以保存类型T的值,或者仅仅保存null。Optional提供很多有用的方法,这样我们就不用显式进行空值检测。
Optional 类的引入很好的解决空指针异常。
package com.demo.optional; import lombok.extern.slf4j.Slf4j; import org.junit.Test; import org.junit.runner.RunWith; import org.springframework.boot.test.context.SpringBootTest; import org.springframework.test.context.junit4.SpringRunner; import java.util.Optional; @Slf4j @RunWith(SpringRunner.class) @SpringBootTest public class TestOptional { @Test public void testOptional() { TestOptional test = new TestOptional(); Integer value1 = null; Integer value2 = new Integer(10); // Optional.ofNullable - 允许传递为 null 参数 Optional<Integer> a = Optional.ofNullable(value1); // Optional.of - 如果传递的参数是 null,抛出异常 NullPointerException Optional<Integer> b = Optional.of(value2); log.info("{}", test.sum(a, b)); } public Integer sum(Optional<Integer> a, Optional<Integer> b) { // Optional.isPresent - 判断值是否存在 log.info("第一个参数值存在: {}", a.isPresent()); log.info("第二个参数值存在: {}", b.isPresent()); // Optional.orElse - 如果值存在返回它,否则返回默认值 Integer value1 = a.orElse(new Integer(0)); //Optional.get - 获取值,值需要存在 Integer value2 = b.get(); return value1 + value2; } }
在Java8之前,任何访问对象方法或属性的调用都可能导致 NullPointerException。如果需要确保不触发异常,就得在访问每一个值之前对其进行明确地检查。
1、明确对象不为null的时候使用of()。如果对象即可能是null也可能是非null,你就应该使用ofNullable()方法。
package com.demo.optional; import com.demo.optional.domain.User; import lombok.extern.slf4j.Slf4j; import org.junit.Test; import org.junit.runner.RunWith; import org.springframework.boot.test.context.SpringBootTest; import org.springframework.test.context.junit4.SpringRunner; import java.util.NoSuchElementException; import java.util.Optional; import static junit.framework.TestCase.assertTrue; import static org.junit.Assert.assertEquals; @Slf4j @RunWith(SpringRunner.class) @SpringBootTest public class OptionalTest { @Test(expected = NoSuchElementException.class) public void whenCreateEmptyOptionalThenNull() { Optional<User> emptyOpt = Optional.empty(); emptyOpt.get(); } //明确对象不为null的时候使用of() @Test(expected = NullPointerException.class) public void whenCreateOfEmptyOptionalThenNullPointerException() { User user = null; Optional<User> opt = Optional.of(user); } //如果对象即可能是null也可能是非null,你就应该使用ofNullable()方法 @Test public void whenCreateOfEmptyOptionalThenNoNullPointerException() { User user = null; Optional<User> opt = Optional.ofNullable(user); } }
2、访问Optional对象的值
package com.demo.optional; import com.demo.optional.domain.User; import lombok.extern.slf4j.Slf4j; import org.junit.Test; import org.junit.runner.RunWith; import org.springframework.boot.test.context.SpringBootTest; import org.springframework.test.context.junit4.SpringRunner; import java.util.NoSuchElementException; import java.util.Optional; import static junit.framework.TestCase.assertTrue; import static org.junit.Assert.assertEquals; @Slf4j @RunWith(SpringRunner.class) @SpringBootTest public class OptionalTest { //从Optional实例中取回实际值对象的方法之一是使用get()方法 @Test public void whenCreateOfNullableOptionalThenOk() { String name = "John"; Optional<String> opt = Optional.ofNullable(name); assertEquals("John", opt.get()); } //get()方法会在值为null的时候抛出异常。要避免异常,你可以选择首先验证是否有值 @Test public void whenCheckIsPresentThenOk() { User user = new User("john@gmail.com", "1234"); Optional<User> opt = Optional.ofNullable(user); assertTrue(opt.isPresent()); assertEquals(user.getEmail(), opt.get().getEmail()); } //检查是否有值的另一个选择是ifPresent()方法。该方法除了执行检查,还接受一个Consumer(消费者)参数,如果对象不是空的,就对执行传入的Lambda表达式 @Test public void whenCheckIfPresentThenOk() { User user = new User("john@gmail.com", "1234"); Optional<User> opt = Optional.ofNullable(user); opt.ifPresent(u -> assertEquals(user.getEmail(), u.getEmail())); } }
3、返回默认值:Optional类提供了API用以返回对象值,或者在对象为空的时候返回默认值。
package com.demo.optional; import com.demo.optional.domain.User; import lombok.extern.slf4j.Slf4j; import org.junit.Test; import org.junit.runner.RunWith; import org.springframework.boot.test.context.SpringBootTest; import org.springframework.test.context.junit4.SpringRunner; import java.util.Optional; import static org.junit.Assert.assertEquals; @Slf4j @RunWith(SpringRunner.class) @SpringBootTest public class OptionalTest { //orElse()如果有值则返回该值,否则返回传递给它的参数值 @Test public void whenEmptyValueThenReturnDefault() { User user = null; User user2 = new User("anna@gmail.com", "1234"); User result = Optional.ofNullable(user).orElse(user2); assertEquals(user2.getEmail(), result.getEmail()); } //如果对象的初始值不是 null,那么默认值会被忽略 @Test public void whenValueNotNullThenIgnoreDefault() { User user = new User("john@gmail.com", "1234"); User user2 = new User("anna@gmail.com", "1234"); User result = Optional.ofNullable(user).orElse(user2); assertEquals("john@gmail.com", result.getEmail()); } //orElseGet()方法会在有值的时候返回值,如果没有值,它会执行作为参数传入的Supplier(供应者)函数式接口,并将返回其执行结果 @Test public void testOrElseGet() { User user = new User("john@gmail.com", "1234"); User user2 = new User("anna@gmail.com", "1234"); User result = Optional.ofNullable(user).orElseGet(() -> user2); assertEquals("john@gmail.com", result.getEmail()); } }
4、orElse()和orElseGet()的不同之处
package com.demo.optional; import com.demo.optional.domain.User; import jdk.nashorn.internal.runtime.regexp.joni.constants.OPCode; import lombok.extern.slf4j.Slf4j; import org.junit.Test; import org.junit.runner.RunWith; import org.springframework.boot.test.context.SpringBootTest; import org.springframework.test.context.junit4.SpringRunner; import java.util.NoSuchElementException; import java.util.Optional; import static junit.framework.TestCase.assertTrue; import static org.junit.Assert.assertEquals; @Slf4j @RunWith(SpringRunner.class) @SpringBootTest public class OptionalTest { /* * 对象为空时,两种方法都调用了createNewUser()方法,这个方法会记录一个消息并返回User对象。 * 当对象为空而返回默认对象时,行为并无差异。 */ @Test public void givenEmptyValueWhenCompareThenOk() { User user = null; log.info("Using orElse"); User result = Optional.ofNullable(user).orElse(createNewUser()); log.info("User orElseGet"); User result2 = Optional.ofNullable(user).orElseGet(() -> createNewUser()); } /* * 两个Optional对象都包含非空值,两个方法都会返回对应的非空值。orElse()方法仍然创建了User对象。与之相反,orElseGet()方法不创建 User 对象。 * 在执行较密集的调用时,比如调用Web服务或数据查询,这个差异会对性能产生重大影响。 */ @Test public void givenPresentValueWhenCompareThenOk() { User user = new User("john@gmail.com", "1234"); log.info("Using orElse"); User result = Optional.ofNullable(user).orElse(createNewUser()); log.info("Using orElseGet"); User result2 = Optional.ofNullable(user).orElseGet(() -> createNewUser()); } private User createNewUser() { log.info("Creating New User"); return new User("extra@gmail.com", "1234"); } }
5、返回异常
除了orElse()和orElseGet()方法,Optional还定义了orElseThrow(),它会在对象为空的时候抛出异常,而不是返回备选的值。
package com.demo.optional; import com.demo.optional.domain.User; import jdk.nashorn.internal.runtime.regexp.joni.constants.OPCode; import lombok.extern.slf4j.Slf4j; import org.junit.Test; import org.junit.runner.RunWith; import org.springframework.boot.test.context.SpringBootTest; import org.springframework.test.context.junit4.SpringRunner; import java.util.NoSuchElementException; import java.util.Optional; import static junit.framework.TestCase.assertTrue; import static org.junit.Assert.assertEquals; @Slf4j @RunWith(SpringRunner.class) @SpringBootTest public class OptionalTest { /* * orElseThrow()方法有更丰富的语义,可以决定抛出什么样的异常,而不总是抛出 NullPointerException。 * 如果 user 值为 null,会抛出 IllegalArgumentException。 */ @Test(expected = IllegalArgumentException.class) public void whenThrowExceptionThenOk() { User user = null; User result = Optional.ofNullable(user) .orElseThrow(() -> new IllegalArgumentException()); } }
6、转换值
package com.demo.optional; import com.demo.optional.domain.User; import lombok.extern.slf4j.Slf4j; import org.junit.Test; import org.junit.runner.RunWith; import org.springframework.boot.test.context.SpringBootTest; import org.springframework.test.context.junit4.SpringRunner; import java.util.Optional; import static org.junit.Assert.assertEquals; @Slf4j @RunWith(SpringRunner.class) @SpringBootTest public class OptionalTest { //如果创建的Optional中的值存在,对该值执行提供的Function函数调用 @Test public void whenMapThenOk() { User user = new User("anna@gmail.com", "1234"); String email = Optional.ofNullable(user) .map(u -> u.getEmail()).orElse("default@gmail.com"); assertEquals(email, user.getEmail()); } //如果创建的Optional中的值存在,就对该值执行提供的Function函数调用,返回一个Optional类型的值,否则就返回一个空的Optional对象 @Test public void whenFlatMapThenOk() { User user = new User("anna@gmail.com", "1234"); user.setPosition("Developer"); String position = Optional.ofNullable(user) .flatMap(u -> u.getPosition()).orElse("default"); assertEquals(position, user.getPosition().get()); } }
7、过滤值
filter()接受一个Predicate参数,返回测试结果为true的值。如果测试结果为false,会返回一个空的Optional。
package com.demo.optional; import com.demo.optional.domain.User; import lombok.extern.slf4j.Slf4j; import org.junit.Test; import org.junit.runner.RunWith; import org.springframework.boot.test.context.SpringBootTest; import org.springframework.test.context.junit4.SpringRunner; import java.util.Optional; import static junit.framework.TestCase.assertTrue; @Slf4j @RunWith(SpringRunner.class) @SpringBootTest public class OptionalTest { @Test public void whenFilterThenOk() { User user = new User("anna@gmail.com", "1234"); Optional<User> result = Optional.ofNullable(user) .filter(u -> u.getEmail() != null && u.getEmail().contains("@")); assertTrue(result.isPresent()); } }
8、Optional类的链式方法
为了更充分的使用Optional,你可以链接组合其大部分方法,因为它们都返回相同类似的对象。
package com.demo.optional; import com.demo.optional.domain.Address; import com.demo.optional.domain.Country; import com.demo.optional.domain.User; import lombok.extern.slf4j.Slf4j; import org.junit.Test; import org.junit.runner.RunWith; import org.springframework.boot.test.context.SpringBootTest; import org.springframework.test.context.junit4.SpringRunner; import java.util.Optional; import static org.junit.Assert.assertEquals; @Slf4j @RunWith(SpringRunner.class) @SpringBootTest public class OptionalTest { @Test public void whenChainingThenOk() { User user = new User("anna@gmail.com", "1234"); String result = Optional.ofNullable(user) .flatMap(u -> u.getAddress()) .flatMap(a -> a.getCountry()) .map(c -> c.getIsocode()) .orElse("default"); String res = Optional.ofNullable(user) .flatMap(User::getAddress) .flatMap(Address::getCountry) .map(Country::getIsocode) .orElse("default"); assertEquals(result, "default"); assertEquals(res, "default"); } }
实体类User:
package com.demo.optional.domain; import lombok.Data; import java.util.Optional; @Data public class User { private Integer id; private String email; private String number; private String position; private Address address; public Optional<Address> getAddress() { return Optional.ofNullable(address); } public Optional<String> getPosition() { return Optional.ofNullable(position); } public User(String email, String number) { this.email = email; this.number = number; } }
实体类Address:
package com.demo.optional.domain; import lombok.Data; import java.util.Optional; @Data public class Address { private Country country; public Optional<Country> getCountry() { return Optional.ofNullable(country); } }
实体类Country:
package com.demo.optional.domain; import lombok.Data; @Data public class Country { private Integer id; private String isocode; }
9、Java9增强
Java9为Optional类添加了三个方法:or()、ifPresentOrElse() 和 stream()。
or()方法与orElse()和orElseGet()类似,它们都在对象为空的时候提供了替代情况。
or()的返回值是由Supplier参数产生的另一个Optional对象。如果对象包含值,则Lambda表达式不会执行。
package com.demo.optional; import com.demo.optional.domain.User; import lombok.extern.slf4j.Slf4j; import org.junit.Test; import org.junit.runner.RunWith; import org.springframework.boot.test.context.SpringBootTest; import org.springframework.test.context.junit4.SpringRunner; import java.util.Optional; import static org.junit.Assert.assertEquals; @Slf4j @RunWith(SpringRunner.class) @SpringBootTest public class OptionalTest { @Test public void whenEmptyOptionalThenGetValueFromOr() { User user=null; User result = Optional.ofNullable(user) .or( () -> Optional.of(new User("default","1234"))).get(); assertEquals(result.getEmail(), "default"); } }
ifPresentOrElse()方法需要两个参数:一个Consumer和一个Runnable。如果对象包含值,会执行Consumer的动作,否则运行Runnable。
如果想在有值的时候执行某个动作,或者只是跟踪是否定义了某个值,那么这个方法非常有用。
package com.demo.optional; import com.demo.optional.domain.User; import lombok.extern.slf4j.Slf4j; import org.junit.Test; import org.junit.runner.RunWith; import org.springframework.boot.test.context.SpringBootTest; import org.springframework.test.context.junit4.SpringRunner; import java.util.Optional; @Slf4j @RunWith(SpringRunner.class) @SpringBootTest public class OptionalTest { @Test public void testIfPresentOrElse() { User user = new User("anna@gmail.com", "1234"); Optional.ofNullable(user).ifPresentOrElse(u -> log.info("User is:" + u.getEmail()), () -> log.info("User not found")); } }
stream()方法把实例转换为Stream对象。Stream的使用带来了其filter()、map()和collect()接口,以获取List。
package com.demo.optional; import com.demo.optional.domain.Address; import com.demo.optional.domain.Country; import com.demo.optional.domain.User; import lombok.extern.slf4j.Slf4j; import org.junit.Test; import org.junit.runner.RunWith; import org.springframework.boot.test.context.SpringBootTest; import org.springframework.test.context.junit4.SpringRunner; import java.util.List; import java.util.NoSuchElementException; import java.util.Optional; import java.util.stream.Collectors; import static junit.framework.TestCase.assertTrue; import static org.junit.Assert.assertEquals; @Slf4j @RunWith(SpringRunner.class) @SpringBootTest public class OptionalTest { @Test public void testStream() { User user = new User("john@gmail.com", "1234"); List<String> emails = Optional.ofNullable(user) .stream() .filter(u -> u.getEmail() != null && u.getEmail().contains("@")) .map(u -> u.getEmail()) .collect(Collectors.toList()); assertTrue(emails.size() == 1); assertEquals(emails.get(0), user.getEmail()); } }
Optional不是Serializable。因此,它不应该用作类的字段。
如果需要序列化的对象包含Optional值,Jackson库支持把Optional当作普通对象。也就是说,Jackson会把空对象看作null,而有值的对象则把其值看作对应域的值。这个功能在jackson-modules-java8项目中。
参考:
https://www.cnblogs.com/zhangboyu/p/7580262.html
https://blog.csdn.net/zknxx/article/details/78586799
以上是关于Java8中的新特性Optional的主要内容,如果未能解决你的问题,请参考以下文章
Java8新特性不了解Optional类,简历上别说你懂Java8!!
Java8新特性不了解Optional类,简历上别说你懂Java8!!
Java8新特性代码示例(附注释)- 方法引用,Optional, Stream
Java8新特性 Optional的正确使用姿势,真的不来稍微了解一下吗?