作为程序员没有遇到坑那是不太可能的,这篇文章记录我遇到的一些坑,避免以后再次遇到。
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的问题都得到圆满解决。