Take a look at the following piece of code:
package test;
import java.util.Date;
public class Test {
public static String format(Date date) {
return String.valueOf(date);
}
public static String format(long millis) {
return format(new Date(millis));
}
public static String format(Instant instant) {
return format(instant == null ? null : instant.getMillis());
}
public static class Instant {
private long millis;
public Instant(long millis) {
this.millis = millis;
}
public long getMillis() {
return millis;
}
}
public static void main(String[] args) {
Instant instant = null;
format(instant);
}
}
Any idea what the output will be? Turns out this throws a
NullPointerException:
Exception in thread "main" java.lang.NullPointerException
at test.Test.format(Test.java:16)
at test.Test.main(Test.java:34)
Interesting. Line 16 is clearly fishy: The idea is that if the
instant is null, the
format(Date) method should be called, while
format(long) should be called otherwise. It turns out things are much more contrived at the bytecode level (use
javap -c):
public static java.lang.String format(test.Test$Instant);
Code:
0: aload_0
1: ifnonnull 8
4: aconst_null
5: goto 15
8: aload_0
9: invokevirtual #35; //Method test/Test$Instant.getMillis:()J
12: invokestatic #41; //Method java/lang/Long.valueOf:(J)Ljava/lang/Long;
15: invokevirtual #46; //Method java/lang/Long.longValue:()J
18: invokestatic #49; //Method format:(J)Ljava/lang/String;
21: areturn
This actually translates to the following Java code, which clearly explains the
NullPointerException:
public static String format(Instant instant) {
return format((instant != null ? Long.valueOf(instant.getMillis()) : null).longValue());
}
Sometimes autoboxing can really rear its ugly head!