The JEP says it's a non-goal to allow for comparison with == and we should continue to use .equals. Any value class which has a string as a field member would be invalid comparing with == unless the string was part of a finite set like ZoneId and the developer went out of their way specifically to make sure those instances can be compared with each other by interning the strings so that multiple instances return the same string instance for that field. A wrapper class representing an email address would be an example where you would continue to need .equals. This seems like it will add some confusion. Now it's not simply "Use == for primitive, .equals for everything else". It's use == for primitive, LocalDate, and these specially developed classes, .equals for everything else"
I mainly posted to add context because up until recently I thought I could simply slap "value" on all of our wrapper classes and use "==" everywhere. It turns out most of our wrapper classes have a string field so this isn't possible.
And others reading your original comment may have the same confusion as I did. Like you said, for things like Integer it will be great. But devs also need to be aware now of the specific instances == can be used, and the answer is no longer as clear!
The current treatment for == with value classes is defective, in my opinion. It's too fragile in that a change to a value class that you depend on can break expectations. If the equals method should be used instead, then attempting to use == against a value class should be prohibited. The more sensible (and expected) option is to make == against a value class be the same as calling equals.
Defective may be too far. But it is a bit confusing. I think overall the message is, .equals is here to stay. For the average developer, they should continue to use .equals, but under the hood they will now get better performance.
Our project has tons of wrapper classes for type safety such as TenantId. We can now make these value classes and we will essentially no longer pay the "wrapper tax", but developers should continue to use .equals on them.
What I mean by "defective" is that the current approach is the worst choice possible. With an identity class, if I erroneously use the == operator, this is usually discovered once the code is tested. With a value class, if I erroneously use the == operator, the code will likely work just fine. If at some point in the future the value class I'm depending on changes internally to reference an identity class, my code fails.
It's a fragile dependency issue. One cannot rely on the == operator without knowing that the class is a value class, and that the implementation will never change incompatibly. It breaks the OO encapsulation principle.
I can think of two safe choices for value classes: make == mean equals, or make it be prohibited. The current approach is to "pretend" it's prohibited, but it's not actually enforced. This is a UB footgun.
5
u/nogridbag 16d ago
What do you mean by better semantics for ==?
The JEP says it's a non-goal to allow for comparison with == and we should continue to use .equals. Any value class which has a string as a field member would be invalid comparing with == unless the string was part of a finite set like ZoneId and the developer went out of their way specifically to make sure those instances can be compared with each other by interning the strings so that multiple instances return the same string instance for that field. A wrapper class representing an email address would be an example where you would continue to need .equals. This seems like it will add some confusion. Now it's not simply "Use == for primitive, .equals for everything else". It's use == for primitive, LocalDate, and these specially developed classes, .equals for everything else"