4

Java introduced value based classes with Java 8 and has only marked few Java.Util (Optional) and Java.time classes with it.

The criteria for value-based classes is defined here.

Most of these criteria (except the strict guidance against using them in identity-sensitive operations and the fact that they should not have accessible constructors) seem to fit for Primitive wrapper classes like Integer, Double (and similar immutable classes like String). I understand technically developers might have used these classes with identity-based operations (although they shouldn't have) and therefore marking them value based now could cause backward-compatibility issues. But apart from this, would there be any other reason that why in concept, these wrapper classes not be value-based?

The reason I incline to think of them as value based.

When you define Integer myInt = 5, you are really just interested in the value 5 and not in the reference holding this value (at least for most of the use cases that I can think of). Likewise when you say String myStr = "hello world", you are really interested in value literal "hello world" and not in it's reference.

Tintin
  • 2,853
  • 6
  • 42
  • 74
  • 4
    Among the listed qualifications: _does not have accessible constructors._ – Louis Wasserman Oct 26 '20 at 23:02
  • yeah, that too.. edited the question. I am interested to know if there any other _fundamental_ reasons? – Tintin Oct 26 '20 at 23:04
  • Upon reading this, I'm curious...what would being able to call an Integer or String value-based buy you? – CryptoFool Oct 26 '20 at 23:29
  • Something seems to not add up when I see the term _value-based class_ and not see `Integer`, `String` be a part of it. I mean for all good purposes doesn't `Integer` act as `int` (keep aside the null value)? `int` is a value and its wrapper would deem fit as a value-based class. I am here for conceptual clarity. Maybe I am missing on something. Or maybe I am not and it's just one of those things where Java would slowly make up in future versions, giving all the legacy code owners a way out. – Tintin Oct 26 '20 at 23:34
  • 1
    *FYI:* `String` is not a "wrapper class". – Andreas Oct 26 '20 at 23:45
  • @Andreas. True. But it is an immutable class and probably fits in the category. but thank for correcting it - edited the question – Tintin Oct 26 '20 at 23:48
  • What "category" would that be? It is not a "wrapper" class, because it's not wrapping a primitive. It's not a "value-based" class, because it does adhere to the **defined rules** of "value-based" classes. – Andreas Oct 26 '20 at 23:53
  • You're asking for conceptual clarity. *Conceptually,* when you define a "label" with rules for what can use that label, any object that doesn't follow the rules cannot use that label. That's the concept of labeling, e.g. for "value-based" classes, "functional" interfaces, "immutable" classes, "inner" classes, or any other kind of "label". – Andreas Oct 26 '20 at 23:53
  • When you define String myStr = "hello world", you are really just interested in the value literal here - "hello world" and not in the reference holding this value (for most of the use cases that I can think of). Likewise when you say Integer myInt = 5, you are really interested in value 5 and not in reference. This category. Edited the question with this. – Tintin Oct 26 '20 at 23:55
  • 1
    @Andreas well, it’s a wrapper around an array, technically… – Holger Oct 27 '20 at 08:46
  • *"The reason **I** incline to think of them as value based"* --- Well, you are entitled to your opinions, but *they* **defined** value based differently. So what you're really asking is: Why did they define it like that? That's an entirely different question, but I'll answer it anyway. – Andreas Oct 27 '20 at 09:29

2 Answers2

4

Basically, you named the most important reason, why retrofitting these classes as value based is not feasible: backward compatibility.

You might have noticed that the constructors of the primitive wrapper types have been deprecated in Java 9, which would be a step into that direction. Still, using identity sensitive operations is only discouraged, not forbidden, so a change that breaks compatibility can not be made on that basis. But potentially breaking identity sensitive operations, would be the only thing that may enable subsequent practical advantages from being a value based class.

For classes like String, BigInteger, and BigDecimal, the JDK developers did not even dare to make the step of deprecating the constructors, most likely because that would be too disrupting. For some constructors, there’s not even an equivalent factory method.

But there’s more than just the public constructors.

See, the documentation of the valueOf methods, the one of Integer exemplary:

This method will always cache values in the range -128 to 127, inclusive, …

So when the factory method is used, you still get a specified identity behavior for some cases.

Which brings us to JLS §5.1.7:

If the value p being boxed is the result of evaluating a constant expression (§15.29) of type boolean, byte, char, short, int, or long, and the result is true, false, a character in the range '\u0000' to '\u007f' inclusive, or an integer in the range -128 to 127 inclusive, then let a and b be the results of any two boxing conversions of p. It is always the case that a == b.

So even the language specifies the behavior of certain identity sensitive operations.

Note that the specification tries not to name the valueOf method that the compiled code will use in practice, but to make up their own rules (which formally only apply to compile-time constants), which did not really pay off. As this answer documents, that part of the specification underwent several rewrites, so when anyone took the wording literally, the guarantees changed over time…

Other guarantees have burned into the developers’ minds much deeper:

JLS §15.29, Constant Expressions:

Constant expressions of type String are always "interned" so as to share unique instances, using the method String.intern.

This is a guaranty about object identity that is impossible to turn down.

Interestingly, JLS §15.18.1 states:

The String object is newly created (§12.5) unless the expression is a constant expression (§15.29).

It’s not clear whether this strict wording is intentional, but as written, it states that for non-constant string concatenation, it must produce a new object with a distinct identity. Yet another specified behavior that developers should not rely on.

So, if someone was to design a new language without legacies, there is nothing wrong with designing these types as value types in the first place. The designer just has to avoid to put all those guarantees into specification that were thought to be a good idea in the past.

Holger
  • 285,553
  • 42
  • 434
  • 765
2

The goal of value-based classes is to lay the foundation for true value types, i.e. types similar to C's struct.

Value types are not objects on the heap, so they are not created using new, and they are not derived from Object, so they don't have object monitors (locks).

That is why the following 2 rules are part of the definition for value-based:

  • make no use of identity-sensitive operations such as reference equality (==) between instances, identity hash code of instances, or synchronization on an instances's intrinsic lock;

  • do not have accessible constructors, but are instead instantiated through factory methods which make no commitment as to the identity of returned instances;

As commented by Brian Goetz on Jan 6, 2015:

Optional is new, and the disclaimers arrived on day 1. Integer, on the other hand, is probably hopelessly polluted, and I am sure that it would break gobs of important code if Integer ceased to be lockable (despite what we may think of such a practice.)


Reference: Value-Based Classes // nipafx

Andreas
  • 154,647
  • 11
  • 152
  • 247