Boxing and Unboxing Surprise in Java
[Java, C#]
I ran into an interesting scenario the other day quite by chance when helping someone understand some code.
Take the following Java code:
void main() {
Integer first = 100;
Integer second = 100;
IO.println(first == second);
first = 200;
second = 200;
IO.println(first == second);
}
This, of course, isn’t how you’d write this code in production, but just to illustrate a point.
This code prints the following:

This was a surprise to me.
Why would two different values return different results for the same logic?
Java, if you are not familiar has two kinds of types:
Primitives consist of byte, short, int, long, float, double, boolean, char.
However, by design, Java converts primitives into equivalent reference types. This is called boxing.
The equivalent reference types for the primitives are Byte, Short, Long, Float, Double, Boolean and Char.
When we write code like this:
Integer first = 100;
We are using the boxed type directly.
Here, first is now an instance of a class, and the logic ==, as you know, compares references, not values.
Which is different from this:
int first = 100;
So technically,
IO.println(first == second);
Should always return false.
But why does it return true with a value of 100 but not 200?
Turns out that such comparisons were such a common scenario, that the Java runtime at startup transparently creates and caches a bunch of integers, ranging from -128 to 127.
So when we use 100, the runtime uses these cached values for comparison.
However, 200 is past the 128 threshold, and so new instances are actually created.
This is why the check returns false for 200.
This code should be written like this:
int first = 100;
int second = 100;
IO.println(first == second);
first = 200;
second = 200;
IO.println(first == second);
Further, if you even find yourself using == for classes, chances are you are doing the wrong thing.
You should instead be using .equals().
This is not the case with C# that does not have this problem.
The equivalent code is as follows:
Int32 first = 100;
Int32 second = 100;
Console.WriteLine(first == second);
first = 200;
second = 200;
Console.WriteLine(first == second);
Again, you would typically not write it this way, as C# does not have boxing. Int32 is an alias for int.
This prints what we expect:

TLDR
Java has some optimizations that change the behaviour of comparison of boxed primitive types.
Happy hacking!