JDK1.7提供的Objects.equals方法,非常方便地实现了对象的比较,有效地避免了繁琐的空指针检查。
在JDK1.7之前,在判断一个短整型、整型、长整型包装数据类型与常量是否相等时,我们一般这样写:
Short shortValue = (short)12345;
System.out.println(shortValue == 12345); // true
System.out.println(12345 == shortValue); // true
Integer intValue = 12345;
System.out.println(intValue == 12345); // true
System.out.println(12345 == intValue); // true
Long longValue = 12345L;
System.out.println(longValue == 12345); // true
System.out.println(12345 == longValue); // true
从JDK1.7之后,提供了Objects.equals方法,并推荐使用函数式编程,更改代码如下:
Short shortValue = (short)12345;
System.out.println(Objects.equals(shortValue, 12345)); // false
System.out.println(Objects.equals(12345, shortValue)); // false
Integer intValue = 12345;
System.out.println(Objects.equals(intValue, 12345)); // true
System.out.println(Objects.equals(12345, intValue)); // true
Long longValue = 12345L;
System.out.println(Objects.equals(longValue, 12345)); // false
System.out.println(Objects.equals(12345, longValue)); // false
为什么直接把==替换为Objects.equals方法会导致输出结果不一样?
通过反编译第一段代码,我们得到语句"System.out.println(shortValue == 12345);"的字节码指令如下:
7 getstatic java.lang.System.out : java.io.PrintStream [22]
10 aload_1 [shortValue]
11 invokevirtual java.lang.Short.shortValue() : short [28]
14 sipush 12345
17 if_icmpne 24
20 iconst_1
21 goto 25
24 iconst_0
25 invokevirtual java.io.PrintStream.println(boolean) : void [32]
原来,编译器会判断包装数据类型对应的基本数据类型,并采用这个基本数据类型的指令进行比较(比如上面字节码指令中的sipush和if_icmpne等),相当于编译器自动对常量进行了数据类型的强制转化。
为什么采用Objects.equals方法后,编译器不自动对常量进行数据类型的强制转化?通过反编译第二段代码,我们得到语句"System.out.println(Objects.equals(shortValue, 12345));"的字节码指令如下:
7 getstatic java.lang.System.out : java.io.PrintStream [22]
10 aload_1 [shortValue]
11 sipush 12345
14 invokestatic java.lang.Integer.valueOf(int) : java.lang.Integer [28]
17 invokestatic java.util.Objects.equals(java.lang.Object, java.lang.Object) : boolean [33]
20 invokevirtual java.io.PrintStream.println(boolean) : void [39]
原来,编译器根据字面意思,认为常量12345默认基本数据类型是int,所以会自动转化为包装数据类型Integer。
在Java语言中,整数的默认数据类型是int,小数的默认数据类型是double。
下面来分析一下Objects.equals方法的代码实现:
Objects.equals方法的代码实现为:
public static boolean equals(Object a, Object b) {
return (a == b) || (a != null && a.equals(b));
}
其中,语句“a.equals(b)”将会使用到Short.equals方法。
Short.equals方法的代码实现为:
public boolean equals(Object obj) {
if (obj instanceof Short) {
return value == ((Short)obj).shortValue();
}
return false;
}
通过代码实现分析:对应语句"System.out.println(Objects.equals(shortValue, 12345));",因为Objects.equals的两个参数对象类型不一致,一个是包装数据类型Short,另一个是包装数据类型Integer,所以最终的比较结果必然是false。同样,语句“System.out.println(Objects.equals(intValue, 12345));”,因为Objects.equals的两个参数对象类型一致,都是包装数据类型Integer且取值一样,所以最终的比较结果必然是true。
(1)保持良好的编码习惯,避免数据类型的自动转化
为了避免数据类型自动转化,更科学的写法是直接声明常量为对应的基本数据类型。
第一段代码可以这样写:
Short shortValue = (short)12345;
System.out.println(shortValue == (short)12345); // true
System.out.println((short)12345 == shortValue); // true
Integer intValue = 12345;
System.out.println(intValue == 12345); // true
System.out.println(12345 == intValue); // true
Long longValue = 12345L;
System.out.println(longValue == 12345L); // true
System.out.println(12345L == longValue); // true
第二段代码可以这样写:
Short shortValue = (short)12345;
System.out.println(Objects.equals(shortValue, (short)12345)); // true
System.out.println(Objects.equals((short)12345, shortValue)); // true
Integer intValue = 12345;
System.out.println(Objects.equals(intValue, 12345)); // true
System.out.println(Objects.equals(12345, intValue)); // true
Long longValue = 12345L;
System.out.println(Objects.equals(longValue, 12345L)); // true
System.out.println(Objects.equals(12345L, longValue)); // true
(2)借助开发工具或插件,及早地发现数据类型不匹配问题
在Eclipse的问题窗口中,我们会看到这样的提示:
本文系作者在时代Java发表,未经许可,不得转载。
如有侵权,请联系nowjava@qq.com删除。