在java1.8版中引入新特性是Optional类,用来解决程序员都会遇到的问题 空指针异常(NullPointerException)
Optional类型被当做更安全的一种方式,用来替代T类型的引用,但是它只有在正确的使用方式下才能更安全,接下来我们看看如果正确的使用。
使用OPTIONAL获取JSON或者对象中的数据
{
"user": {
"age": 20
,"name": "Jim"
,"address": {
"province": "浙江省"
,"postcode": "111111"
}
}
}
获取postcode的值
或获取对象的属性
user.getAddress().getProvince().getPostCode()
如果使用平常的方法,需要判断对每个值都继续判断,否则程序可能会出现空指针异常。
if (user != null) {
address = user.getAddress();
if (address != null) {
province = address.getProvince();
if (province != null) {
postCode = province.getPostCode();
}
}
}
这样的代码就非常的冗余并且难以维护。
但是如果我们利用Optional则清晰并方便的获取到想要的属性值。
首先先创建 Optional的对象
调用empty() 方法创建一个空的Optional实例
Optional<User> empty = Optional.empty();
User user = empty.get();
调用此方法,程序会抛出了NoSuchElementException
Optional创建对象的方法有2种,
static <T> Optional<T> of(T value)
static <T> Optional<T> ofNullable(T value)
这2钟创建方法有什么不同呢,
Optional<User> userOpt = Optional.of(user);
用of()方法创建时程序会抛出NullPointerException,还是没有摆脱空指针异常,所以当你明确知道对象不为null的时候才使用of()
Optional<User> userOpt = Optional.ofNullable(user);
如果使用ofNullable()则会产生一个空Optional
有了实例之后就可以访问对象里面的值
获取值的方法其中之一的是 T get(),get方法会在Optional值存在的时候获取对象其中的元素,但是在对象为Null的时候回抛出NoSuchElementException异常。
例如:
Optional<User> empty = Optional.empty();
User user = empty.get();
如果想避免这种情况的话需要先判断Optional对象是否有值。
利用boolean isPresent();
if (userOpt.isPresent()){
User user = userOpt.get();
}
如果需要对象可能空值,但是又需要程序继续进行,可以给默认值
T orElse(T other)
T orElseGet(Supplier<? extends T> other)
<X extends Throwable> T orElseThrow(Supplier<? extends X> exceptionSupplier)
api里面提供了3种方法
第一种的使用
User user=null;
User user2=new User();
User userObject = Optional.ofNullable(user).orElse(user2);
这里可以发现返回了一个默认值的user2
第二种的使用,是可以在参数中传递一个函数
例如:
User userObject = Optional.ofNullable(user).orElseGet(() -> new User());
或
User userObject = Optional.ofNullable(user).orElseGet(() -> user2);
orElse() 和 orElseGet() 的不同之处
看起来两者的使用好像很相似,但是大有不同
User user=null;
User user2=new User();
System.out.println("orElse");
User userObject = Optional.ofNullable(user).orElse(println());
System.out.println("orElseGet");
User userObject2 = Optional.ofNullable(user).orElseGet(()->println());
public User println(){
System.out.println("被执行到");
return new User();
}
执行结果:
orElse
被执行到
orElseGet
被执行到
如果不为空的情况
User user=new User();
User user2=new User();
System.out.println("orElse");
User userObject = Optional.ofNullable(user).orElse(println());
System.out.println("orElseGet");
User userObject2 = Optional.ofNullable(user).orElseGet(()->println());
执行结果:
orElse
被执行到
orElseGet
可以通过这里例子得出
不管前面判空是不是空,orElse都会执行,orElseGet则只在对象为空的时候才执行
使用orElseGet可以节省一个生成对象的性能,在高并发的情况下,可以节省大量的性能
在第三种备选值是可以在对象为空的时候抛出异常,但是同样里面可以写函数,自定义一些异常
值的转换和过滤
如果使用过stream流操作这块就非常熟悉了,optional 可以使用和stream中一样的方法来操作数据
我们先看下值的转换 map()和flatMap()
User user=new User("zhangsna",23);
String name=Optional.ofNullable(user).map(u->u.getUserName()).orElse("lisi");
使用flatMap()我们先写一个方法
static String Optional<String> getPosition(String name)() {
return Optional.ofNullable(name );
}
使用
String name = Optional.ofNullable(user)
.flatMap(u -> u.getName()).orElse("lisi");
2者的结果是一样的
对于map和flatmap不同:
map是把结果自动封装成一个Optional,但是flatmap需要你自己去封装。
map的输出对应一个元素,必然是一个元素(null也是要返回),flatmap是0或者多个元素(为null的时候其实就是0个元素)。
这样flatmap的使用意义就在于,一般的java方法都是返回一个结果,但是对于结果数量不确定的时候,用map这种java方法的方式,是不太灵活的,所以引入了flatmap。
过滤值
除了转换值之外,Optional 类也提供了按条件“过滤”值的方法。
filter() 接受一个 Predicate 参数,返回测试结果为 true 的值。如果测试结果为 false,会返回一个空的 Optional。
User user=new User("zhangsna",23);
Optional<User> result =Optional.ofNullable(user).filter(u->u.getUserName()!=null&&u.getAge>0);
掌握了这些内容之后,我们可以开始实现我们最开始的需求
String result = Optional.ofNullable(user)
.flatMap(User::getAddress)
.flatMap(Address::getProvince)
.map(Country::getPostCode)
.orElse("default");
是不是代码,减少了很多冗余变得简洁清晰了很多呢
在结尾处贴一个java核心技术卷 II 第十一版中关于演示Optional的使用事例来供大家学习
public static void main(String[] args) throws IOException {
Optional<User> empty = Optional.empty();
User user = empty.get();
Optional<User> userOpt = Optional.of(user);
if (userOpt.isPresent()){
userOpt.get();
}
String contents = new String(Files.readAllBytes(
Paths.get("../gutenberg/alice30.txt")), StandardCharsets.UTF_8);
List<String> wordList = Arrays.asList(contents.split("\\PL+"));
Optional<String> optionalValue = wordList.stream()
.filter(s -> s.contains("fred"))
.findFirst();
System.out.println(optionalValue.orElse("No word") + " contains fred");
Optional<String> optionalString = Optional.empty();
String result = optionalString.orElse("N/A");
System.out.println("result: " + result);
result = optionalString.orElseGet(() -> Locale.getDefault().getDisplayName());
System.out.println("result: " + result);
try {
result = optionalString.orElseThrow(IllegalStateException::new);
System.out.println("result: " + result);
} catch (Throwable t) {
t.printStackTrace();
}
optionalValue = wordList.stream()
.filter(s -> s.contains("red"))
.findFirst();
optionalValue.ifPresent(s -> System.out.println(s + " contains red"));
HashSet<String> results = new HashSet<String>();
optionalValue.ifPresent(results::add);
Optional<Boolean> added = optionalValue.map(results::add);
System.out.println(added);
System.out.println(inverse(4.0).flatMap(OptionalApi::squareRoot));
System.out.println(inverse(-1.0).flatMap(OptionalApi::squareRoot));
System.out.println(inverse(0.0).flatMap(OptionalApi::squareRoot));
Optional<Double> result2 = Optional.of(-4.0)
.flatMap(OptionalApi::inverse).flatMap(OptionalApi::squareRoot);
System.out.println(result2);
}
public static Optional<Double> inverse(Double x) {
return x == 0 ? Optional.empty() : Optional.of(1 / x);
}
public static Optional<Double> squareRoot(Double x) {
return x < 0 ? Optional.empty() : Optional.of(Math.sqrt(x));
}