Optional类是在Java 8中引入的,用于解决NullPointerException的问题。
java.util.Optional类是一个泛型类型的类,只包含一个类型为T的值。其目的是提供对可能为null的类型T的引用对象的更安全的替代方案。但是,只有在正确使用的情况下,Optional对象才是安全的。
创建Optional对象的方式:
Empty Optional
使用Optional.empty(),我们可以创建一个不包含任何类型的空的optional对象。
//returning a empty optional of type string
public static Optional<String> emptyOptional(){return Optional.empty();
}//or
Optional<String> str = Optional.empty();
Nullable Optional
使用Optional.ofNullable()方法,我们可以创建可空的Optional对象。这些对象可以是null,也可以包含值。
public static Optional<Wallet> getWallet(){return Optional.ofNullable(null);
}
Non-Nullable Optional
使用该Optional.of()方法我们可以创建不可为 null 的可选对象。
public static Optional<Wallet> getWallet(){return Optional.of(new Wallet(100));
}
现在让我们看一下Optional类中的不同方法,我们可以利用这些方法来编写促进空安全的清晰代码。
在Optional类中的 get() 方法用于获取值,如果存在的话,否则抛出NoSuchElementException异常。
例如,假设我们有一个具有私有字段money的Wallet对象。要获取money的值,我们可以按如下方式操作:
//normal way to get money
int savings = wallet.getMoney();//when wallet object is wrapped in a optional
int savings = optionalWallet.get().getMoney();
如果wallet对象为null,第二种方式比第一种方式更安全,因为它会抛出NoSuchElementException而不是NullPointerException。
我们可以通过使用isPresent()方法来改进第二种方式,该方法返回一个布尔值,指示值是否存在。
List<Integer> savings = new ArrayList();
if(optionalWallet.isPresent()){savings.add(optionalWallet.get().getMoney());
}
但与此同时,这种方式并没有比检查非空更为简洁。
List<Integer> savings = new ArrayList();
if(wallet != null){savings .add(wallet.getMoney());
}
为了使代码更简洁清晰,我们可以使用ifPresent()方法,该方法接受一个Consumer或一个Runnable,或者两者都接受。使用ifPresent(),我们可以消耗一个正确的值或生成一个替代值。让我们看一下具体的用法。
List<Integer> savings = new ArrayList();
optionalWallet.ifPresent(wallet -> savings.add(wallet.getMoney()));
在上面的示例中,只有当wallet不为null时,即存在时,才将money添加到savings中。
如果我们将wallet本身添加到列表中。
//using lambda
List<Wallet> wallets = new ArrayList<>();
optionalWallet.ifPresent(wallet -> wallets.add(wallet));//you can also use method reference
List<Wallet> wallets = new ArrayList<>();
optionalWallet.ifPresent(wallets::add);
正如你所看到的,这比一开始的示例要好得多。
在某些情况下,当optional为空时,你可能希望有一个默认值。使用orElse()方法,我们可以在optional为null时生成一个替代值。让我们看一个例子。
List<Wallet> wallets = new ArrayList<>();
Wallet wallet = optionalWallet.orElse(new Wallet(0));
wallets.add(wallet);
在上面的例子中,如果optionalWallet包含一个值,它将被赋给wallet变量;如果没有值,则分配一个值为0的默认wallet。orElse()方法可以与任何其他类型一起使用,生成默认值。
你还可以在orElseGet()方法内调用一块代码来计算一个默认值。
//calculating a default value
Wallet wallet = optionalWallet.orElseGet(() -> {int randomAmount = (int) (Math.random() * 500); // Generating a random amount between 0 and 500return new Wallet(randomAmount);
});
在使用Spring Boot应用程序和数据库时,当我们执行选择查询以获取用户时,一种良好的实践是将用户对象封装在Optional中,然后我们可以这样做:
//Getting the user from database
Optional<User> findUserById(String id);//Calling the find user method
User user = findUserById(id).orElseThrow(UserNotFoundException::new);
在上面的例子中,你可以看到,我们不需要进行任何检查来确定用户是否存在。如果数据库中存在用户,我们将获取该用户;如果不存在,我们将在那里优雅地抛出一个异常。