To see why, consider that to do any useful work, data from different objects (also from different types) has to be combined. To be able to do that in the OOP framework, the encapsulation has to be unwrapped. That's why Java code is littered with getters and setters that don't do any useful work at all, they just make it too painful to get any real work done.
Again, there is a place for objects and implementation hiding, but it's at the highest levels of an architecture where different components get integrated.
This also has huge implications in a language that emphasises dynamic loading like Java. And it also flies in the face of all of the pretenses that ABI compatibility is sacrosanct and no feature that breaka it can be considered, that the design team often touts.
But I'd say that GP's complaint about inequality leaking makes no sense anyways, because what could be more unequal than different implementation, or different internal state implying different behavior down the line? The public subset isn't some arbitrary interface that could have different implementations. And even then, "equals under interface I1" would have to be considered a very special type of "equality", not the general case.
It should work even for strings: They will surely continue to be heap-allocated, and memcmp-ing pointers (inside the new "structs") is exactly an identity comparison.
For example, you might have a value class for representing (limited-precision) fractions using two longs internally, for the numerator and denominator. For efficiency trade-off reasons, you don’t want to always shorten the fraction. But now client code can distinguish 2/3 from 4/6 using ==.
Scenarios of that sort are conceivable where this actually leaks sensitive information. In any case, it creates dependencies on implementation details where you don’t want to have them.
When designing a value class, you are now in the dilemma of either always having to normalize the representation, costing performance, or having your class be a funnel for leaking implementation details.
There is a lot wrong with that: complexity, bloat, and slowness.
> But now client code can distinguish 2/3 from 4/6 using ==
That's a great way to obfuscate code. Not a good idea. The right way to do the comparison is, just make a function called CompareRational().
Java separates checking identity and equality for objects. == basically checks if two pointers are the same. Equality is a subjective concept based on an interface (ie equals/hashCode). So this means:
new Integer(1000) == new Integer(1000) // true, used to be false
new Integer(1000).equals(new Integer(1000)) // true
new Integer(10) == new Long(10) // compiler error, used to false
new Integer(10) == new Integer(10) // true
There's a lot going on here. The complication is that in previous versions of Java (and I'm not sure when this changed), integers below a certain value would be replaced with canonical types below a certain value. I think it was 128 but its's been awhile. This led to the difference between 10 and 1000. That's now changed, I suspect because the above comparisons are being implicitly unboxed. That didn't used to happen either. I saw this because the Integer/Long comparison used to return false and it's now a compiler error so there must be unboxing going on.You may still be able to get the old behavior through variables too.
Anyway, if value classes lose identity then == changes from pointer equality to bitwise equality. That will hopefully resolve a bunch of corner cases like this but it is a breaking change, technically.
new Integer(10) == new Integer(10) // true
Before value classes this would always be false. The only time comparing Integer objects with == could be true is if Integer object was create by going through Integer.valueOf (or obviously if they were the same object reference.) By default the cached values where -127 to 127, but that is tuneable at runtime.https://github.com/openjdk/jdk/blob/jdk-27%2B27/src/java.bas...
iconst_1
invokestatic java/lang/Integer.valueOf:(I)Ljava/lang/Integer> By default, Java maintains a cache of Integer objects for values between -128 and +127.
[1]: https://stackoverflow.com/questions/3130311/weird-integer-bo...
[2]: https://dev.to/marzuk16/understanding-integer-caching-in-jav...
Years before the autoboxing/Integer.valueOf() caching stuff (and before generics), (I) used to have IntegerProvider that did similar stuff to higher ranges. Personally, I have considered autoboxing on integers net-negative for Java