It has become pretty common in some OOP languages to benefit from the best of both worlds :
@Value
(Lombok), or more recently just record
) to avoid "primitive obsession" and pass around simple value wrappers. We can get the added benefit of potentially doing some extra validation in their constructor, but the core capability is the direct value comparison of two different objects.in C#, nowadays point 2 is easy to achieve with implicit
and similar techniques. But I wouldn't know how to do it in Java, as one cannot inherit custom classes from native classes Integer
or Long
.
An example :
// This is technically not an Integer, but a value type object (record)
public record PositiveInteger(Integer value) {
public PositiveInteger{
if (value < 0) {
throw new IllegalArgumentException();
}
}
}
...
// This expects an Integer. I should not be able to pass a PositiveInteger
public static printInteger(final Integer someInteger) {
print(someInteger.toString());
}
...
public static void main() {
Integer i = 1234;
printInteger(i);
PositiveInteger x = new PositiveInteger(6789);
printInteger(x); // <-- the hard part: effortless conversion
from my custom type to Integer,
without having to change the printInteger method
}
How do you get as close to this as possible in Java? the ultimate goal is to pass around PositiveInteger
values with as little hassle as possible, as if they were simply Integer
s.
How do you get as close to this as possible in Java? the ultimate goal is to pass around
PositiveInteger
values with as little hassle as possible, as if they were simplyIntegers
.
But they cannot behave as if they were Integer
s because, as you are aware, Integer
is final
. All objects of type Integer
have class Integer
. This was an intentional design decision, and it is unlikely to be relaxed. Furthermore, Java autoboxing works only with the standard primitive-type wrapper classes, so if you pass around values of type PositiveInteger
then you'll need to invoke a method or read a field to get at the wrapped int
. I guess one could hope for some movement on that side, but I wouldn't hold my breath.
"As close as possible" depends on how you judge. For example,
if autoboxing is a requirement then you really have only one alternative in the language and standard library itself: use java.lang.Integer
(or one of the other standard wrapper classes).
for "close" in a class hierarchy sense, with your own PositiveInteger
class, you can write that as a subclass of java.lang.Number
. The wrapped int
value would be provided by its intValue()
method, and you could provide a method to convert directly to Integer
, too, if you wanted to do. This would not get you autoboxing, but it would get you some interoperability that you would not have otherwise. Your class can be immutable, of course.
for PositiveInteger
as a record class, you've pretty much already chosen all the relevant characteristics. You get a class that does not support autoboxing, and whose closest common ancestor with Integer
is Object
.
There is no such annotation in the Java standard library. Maybe you mean Lombok's @Value
annotation, which can have an effect similar to using a record class when combined with an annotation processor that supports it. Or maybe some other third-party annotation. Any way around, that would be a third-party add-on, not part of Java proper.
There is a concept of "value classes" in the process of being introduced into Java, in Project Valhalla. But this is primarily about object (lack of) identity, not about autoboxingt. The plan appears to be to, eventually, convert the standard wrapper classes into value classes, but that will not help you.
Jakarta EE (formerly Java EE) offers Jakarta Validation, formerly known as Bean Validation. This technology provides, among other things, annotations by which you could express constraints such as the basic one of your PositiveInteger
class. Supposing that you were working in an environment where this API is available, you could get some of what you want by applying appropriate annotations, such as @Min
.
@Min(0)
int countGuests;