You have stumbled across the intricacies of how the Java compiler optimizes String
.
Suppose I have this program:
String a = "abc";
String b = "abc";
Here the compiler can initialize a
and b
to the same String
instance. This entails that a == b
and a.equals(b)
.
Here we also get the same behaviour:
String a = "abc";
String b = "ab" + "c";
This is because "ab" + "c"
can be evaluated at compile-time to just "abc"
, which in turn can share an instance with a
.
This technique is not possible with expressions that call functions:
String a = "abc";
String b = "ab" + functionThatReturnsC();
This is because functionThatReturnsC
could have side-effects which cannot be resolved at compile-time.
Your case of returnVal
is interesting. Since it is constant, it could be inlined, in which case the compile-time instance sharing could be applied. It seems the compiler implementer decided not to support this.
This issue exposes a weakness of Java. Since we cannot override =
, programmers cannot implement custom value-types. Therefore, you should always use equals
or Objects.equals
to ensure consistent behaviour.
Note that these optimizations may differ between compilers.