用語 NPE:NullPointerException
🏃♂️はじめに
たとえば以下のように、
String型の値を返すメソッド getMailTemplateByClient
を作っていて、呼び出し元で返り値を使っているとします(mailTemplate
にセット)。
public Client getClientInfo(int clientId) {
// メソッドコール
String mailTemplate = getMailTemplateByClient(clientId);
int mtCharCount = mailTemplate.length();
//(以下略)
}
// 顧客に送るメール定型文を取得する
private String getMailTemplateByClient(int clientId) {
// 顧客IDに紐づいたメール定型文をDBから取得する
final String mailTemplate = selectMailTemplateFromClientTbl(clientId);
return mailTemplate;
}
ところがところが、
単体テストをしていく中で上記コードの.length()
でNPEが生じるケースがあり、実はgetMailTemplateByClientメソッドはnullを返す可能性があることが分かりました😨
ということで以下のようにnullを想定した条件分岐を追加することでNPEを回避しました!
めでたしめでたし👍
public Client getClientInfo() {
// メソッドコール
String mailTemplate = getMailTemplateByClient(clientId);
// int mtCharCount = mailTemplate.length();
int mtCharCount = 0;
if(mailTemplate != null) {
mtCharCount = mailTemplate.length();
}
}
...ではないです。(動きはするけど)
これでは将来、追加の要望対応とかでgetMailTemplateByClientを再利用するときに、また同じワナを踏む可能性があります。
とくに再利用を考えている第三者をも不幸にしてしまいます😥(バグの温床)
✅Optionalを使って解決!
Java(8~)には以下のような素敵なクラスがあることを知りました。
java.util.Optional
参考: https://docs.oracle.com/javase/jp/8/docs/api/java/util/Optional.html
これを踏まえてgetMailTemplateByClientメソッドを以下のように修正することで、
その返り値はnullかもしれないことを表現しました。
これで再利用を考えている第三者にもその可能性を伝えることができ、未来のNPE撲滅に貢献できます👌
// 顧客に送るメール定型文を取得する
private Optional<String> getMailTemplateByClient(int clientId) {
// 顧客IDに紐づいたメール定型文をDBから取得する
final Optional<String> mailTemplate = selectMailTemplateFromClientTbl(clientId);
return mailTemplate;
}
また呼び出し元は以下のようにmap
メソッドを使ってOptinalの値を取り出すように修正しました。
(そして元のif文で書いていた頃よりもスッキリとなった😄)
public Client getClientInfo() {
// メソッドコール
Optional<String> mailTemplate = getMailTemplateByClient(clientId);
int mtCharCount = mailTemplate.map(mt -> mt.length()).orElse(0);
//(以下略)
}
(補足)
今回は取り出した値を返して mtCharCount にセットするためmapメソッドを使いますが、
値を返さないケースではifPresent
メソッドを使います。
⚠ 注意
mailTemplate.get().length()
のように、値の取り出しにget()は使わない方が良いです。
get()は中身があることを前提としており、中身が空なら例外を吐いてしまうためです。
🍜〆
- nullの可能性があることはOptional型を使うことで表現する。
- Optionalの値の取り出しには map / ifPresent メソッドを使う。