作为程序员没有遇到坑那是不太可能的,这篇文章记录我遇到的一些坑,避免以后再次遇到。
1、比较两个Integer类型的值是否相等使用“==”
缘起:一条lambda表达式引起的BUG, 这条表达式是从List里找出对应ID的记录。
UserAccountInfo userInfo = list.Stream().filter(r->r.getId()==userId).findFirst().orElse(null);
单元测试时,是ok的(单元测试时userId=4)。上了测试环境甚至上到生产环境才发现这是一个大BUG. 我们来分析一下 “==”, 这是比较的内存地址, 两个值相同的Integer内存地址不一定相同。
看以下例子:
public static void main(String[] args) {
//1
Integer a= 100, b=100;
System.out.println(a==b); //true
//2
a=100;
b=new Integer(100);
System.out.println(a==b); //false
//3
a=200;
b=200;
System.out.println(a==b); //false
}
这是享元模式在作怪,不知道的可以看看Integer的源码,这里不做详细介绍。
那么我们在程序里如何比较两个Integer值是否相等呢? 有以下几种方法
System.out.println(a.intValue()==b);
System.out.println(a.compareTo(b)==0);
System.out.println(a.equals(b));
采用第一种方法的一看就知道刻意为了避规享元模式造成的影响, 将包装类型的值换成基本类型来比较,但是这样的代码看起来有点low, 或者说有点作(好像刻意让人知道你懂享元模式似的,这本身不是啥高深的东西,有啥好炫耀的)。
第二种方法是直接比较值,第三种方法利用Object的equals方法,当然也是比较值, 我个人推荐第二种方式来比较,因为从可读性来说是比较可取的。
结论:比较两个Integer类型是否相等时建议使用 compareTo 方法。 这条规则对Short、 Long类型同样适用。
2、Optional的get()、orElseGet()方法
高手请略过, 出现这种问题的一般会是刚使用Optional又没有看过Optional源码或者读过相关文档的朋友,我也是其中之一。
缘起:参考第一条的lambda表达式
UserAccountInfo userInfo = list.Stream().filter(r->r.getId()==userId).findFirst().get();
"=="的问题还未解决的时候, r.getId()==userId 会返回false,所以导致根据userId查不到数据, 报 NosuchElementException。
我们又修改了代码
Optional<UserAccountInfo> userInfo = list.Stream().filter(r->r.getId()==userId).findFirst();
if(userInfo==null) return null;
return userInfo.get();
还是报NosuchElementException,查看Optional的源代码才发现,lambda表达式返回的Optional是不会为空,所以和没修改前是一样的,都会执行get()方法。
继续看源代码, 发现有个orElseGet()、还有个orElse()方法, orElseGet()方法会调用传入参数的get()方法,所以参数不能传null,否则会报空指针。 orElse();方法会直接返回传入的值。 源码如下
/**
* Return the value if present, otherwise return {@code other}.
*
* @param other the value to be returned if there is no value present, may
* be null
* @return the value, if present, otherwise {@code other}
*/
public T orElse(T other) {
return value != null ? value : other;
}
/**
* Return the value if present, otherwise invoke {@code other} and return
* the result of that invocation.
*
* @param other a {@code Supplier} whose result is returned if no value
* is present
* @return the value if present otherwise the result of {@code other.get()}
* @throws NullPointerException if value is not present and {@code other} is
* null
*/
public T orElseGet(Supplier<? extends T> other) {
return value != null ? value : other.get();
}
最终我们改成
UserAccountInfo userInfo = list.Stream().filter(r->r.getId().compareTo(userId)).findFirst().orElse(null);
“==”和Optional的问题都得到圆满解决。