不好的做法
在定義并實(shí)現(xiàn)一個(gè)方法時(shí)藕漱,如果返回值是一個(gè)引用類型的對(duì)象,那么這個(gè)對(duì)象的值是有可能為null的,而方法的調(diào)用者在調(diào)用的時(shí)候就需要考慮返回值為null的情況,進(jìn)行單獨(dú)處理娩践。這樣就常常會(huì)出現(xiàn)類似于如下的代碼:
public static void main(String[] args) {
List<String> stringList = getList();
if(stringList != null){
System.out.println("Size of stringList :" + stringList.size());
}else{
System.out.println("Size of stringList :" + 0);
}
}
public static List<String> getList() {
return null;
}
可以看到對(duì)于方法的調(diào)用者而言,需要考慮返回值為null的情況烹骨,這樣的設(shè)計(jì)不利于代碼的整潔翻伺。
方法應(yīng)盡量返回空集合或空對(duì)象,而非null
比較好的做法是展氓,對(duì)于方法的定義和實(shí)現(xiàn)者而言穆趴,盡量保證方法的返回值不返回null值,而是返回相應(yīng)的“空對(duì)象”來(lái)代替null值遇汞。比如下面的代碼:
static List<String> getList2(){
List<String> stringList = null;
if(stringList == null){
return Collections.EMPTY_LIST;
}
return stringList;
}
static List<String> getList3(){
List<String> stringList = null;
return Optional.fromNullable(stringList).or(Collections.EMPTY_LIST);
}
public static void main(String[] args) {
List<String> stringList2 = getList2();
System.out.println("Size of stringList2 :" + stringList2.size());
List<String> stringList3 = getList3();
System.out.println("Size of stringList3 :" + stringList3.size());
}
這樣的實(shí)現(xiàn)有兩種方式:
- 如
getList2()
所示:直接判斷返回值是否==null,如果是簿废,則返回一個(gè)空集合空入。這樣的做法很直觀,但不推薦族檬。在程序中應(yīng)盡量避免對(duì)null值的直接判斷歪赢。 - 如
getList3()
所示:通過(guò)使用Guava的Optional工具來(lái)對(duì)返回的對(duì)象進(jìn)行處理。推薦這種做法
同樣道理单料,對(duì)于返回值是String類型時(shí)埋凯,如果遇到null值,可以返回空串("")代替之扫尖。當(dāng)然白对,同樣可以使用通用的Guava庫(kù)來(lái)返回默認(rèn)值(String也是對(duì)象),也可以直接使用StringUtils來(lái)返回默認(rèn)的字符串换怖。如下面的例子:
public static void main(String[] args) {
String string1 = getString1();
System.out.println("string1 :" + string1);
}
static String getString1(){
String string = null;
return Optional.fromNullable(string).or(StringUtils.EMPTY);
// or ...
// return StringUtils.defaultString(string);
}
對(duì)于需要區(qū)分null值的和空值的方法甩恼,將返回值的類型用Optional包裝,而非直接返回
對(duì)于有些場(chǎng)景沉颂,方法的調(diào)用者有可能需要區(qū)分null值和空值來(lái)進(jìn)行不同的行為条摸,那么也不要直接返回,而是通過(guò)Guava的Optional工具進(jìn)行包裝铸屉。如下面的例子所示:
public static void main(String[] args) {
List<String> stringList1 = getList1().or(Collections.EMPTY_LIST);
System.out.println("Size of stringList1 :" + stringList1.size());
}
static Optional<List<String>> getList1(){
// 從數(shù)據(jù)庫(kù)里取到null值
List<String> stringList = null;
return Optional.fromNullable(stringList);
}
這樣做的好處是钉蒲,方法的調(diào)用者拿到的是返回值包裝后的對(duì)象,它需要被解包后才能使用彻坛。這樣可以迫使方法的調(diào)用者考慮對(duì)象缺失-absent(null值)的情況顷啼,而不是忘記處理后帆赢,在運(yùn)行時(shí)再拋出NullPointerException。
其它好的實(shí)踐
- 在對(duì)String進(jìn)行操作的時(shí)候线梗,盡量使用一些第三方的庫(kù)(如:Apache commons-lang3)椰于。因?yàn)檫@些庫(kù)方法的實(shí)現(xiàn)中,處理了null值的情況仪搔。
- 在對(duì)字符串進(jìn)行
String.equals()
方法進(jìn)行兩個(gè)字符串判等的時(shí)候瘾婿,將已知的字符串放在前面,將未知的變量作為方法的參數(shù)烤咧,放在后面偏陪。
如下面的代碼會(huì)拋出NullPointerException:
String knownStr = "123";
String unknownStr = null;
if(unknownStr.equals(knownStr)){
// ... do something
}
如果將knownStr放在前面,就可以避免這個(gè)異常:
String knownStr = "123";
String unknownStr = null;
if(knownStr.equals(unknownStr)){
// ... do something
}
- 在定義POJO的時(shí)候煮嫌,將所有的域都初始化合理的默認(rèn)值笛谦。
- 如果對(duì)于一個(gè)對(duì)象obj,要取其對(duì)應(yīng)的字符串的值昌阿,那么盡量使用
String.valueOf(obj)
饥脑,而非obj.toString()
。
public static String valueOf(Object obj) {
return (obj == null) ? "null" : obj.toString();
}
源碼可見懦冰,String.valueOf(obj)
進(jìn)行了null值的判斷灶轰,對(duì)于null值返回字符串"null"。這樣雖然可以避免異常刷钢,但是未必符合業(yè)務(wù)需要笋颤。因此,推薦使用StringUtils的defaultString()
方法來(lái)處理内地,其專門用于處理String為null的情況的缺省值:
public static String defaultString(final String str) {
return str == null ? EMPTY : str;
}
public static String defaultString(final String str, final String defaultStr) {
return str == null ? defaultStr : str;
}