最近接到一个需求,要将某个字段从int转成long,我一看这部很简单 卡卡全文搜索,替换,解决编译问题,直接mr,发版! 然后到了金丝雀的时候,突然同事说,你代码npe了,好多,快回滚qaq
看错误日志之后,发现出问题的代码大概是这一个样子的:
java
public static void Int2Long2() {
Integer a = null;
Long b = null;
Long c = a != null && a > 0 ? a : b;
System.out.println("int to long: " + c);
}
原本a和b都是int型, 需求要求我将b转成long类型的,所以我直接修改了声明时的类型,然后顺手改了下面c的类型 一看没有编译问题,就直接不管了,但是这里有一个坑
如果一个对象编译器可以推导出是初始化为null,那么编译之后就是 Ojbect
类型,而非声明的类型 如果基本类型的包装类型之间存在不一致的场景,默认是会通过基本类型来转换
所以对于上面的代码,编译之后的是:
java
public static void Int2Long2();
descriptor: ()V
flags: (0x0009) ACC_PUBLIC, ACC_STATIC
Code:
stack=2, locals=3, args_size=0
0: aconst_null
1: astore_0
2: aconst_null
3: astore_1
4: aload_0
5: ifnull 23
8: aload_0
9: invokevirtual #11 // Method java/lang/Integer.intValue:()I
12: ifle 23
15: aload_0
16: invokevirtual #11 // Method java/lang/Integer.intValue:()I
19: i2l
20: goto 27
23: aload_1
24: invokevirtual #8 // Method java/lang/Long.longValue:()J
27: invokestatic #9 // Method java/lang/Long.valueOf:(J)Ljava/lang/Long;
30: astore_2
31: getstatic #5 // Field java/lang/System.out:Ljava/io/PrintStream;
34: aload_2
35: invokedynamic #10, 0 // InvokeDynamic #1:makeConcatWithConstants:(Ljava/lang/Long;)Ljava/lang/String;
40: invokevirtual #7 // Method java/io/PrintStream.println:(Ljava/lang/String;)V
43: return
LineNumberTable:
line 30: 0
line 31: 2
line 32: 4
line 33: 31
line 34: 43
StackMapTable: number_of_entries = 2
frame_type = 253 /* append */
offset_delta = 23
locals = [ class java/lang/Integer, class java/lang/Long ]
frame_type = 67 /* same_locals_1_stack_item */
stack = [ long ]
}
或者说是:
java
public static void Int2Long2() {
Object var0 = null;
Object var1 = null;
Long var2 = var0 != null && var0 > 0 ? (long)var0 : var1;
System.out.println("int to long: " + var2);
}
注意其中的 /Long.longValue:()J
因为存在 (long)
来转换,所以一定要后面的变量非null才可以,否者就是会npe 而这种一串逻辑中存在隐式的类型转换的场景,很可能就会因为校验不严谨而出问题,最好还是一个个判断转换。