java

Implicit casts in Java (passing value type wrappers to methods expecting a regular Integer or Long)


It has become pretty common in some OOP languages to benefit from the best of both worlds :

  1. The best of value-type objects (in Java that would be @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.
  2. The best of implicit type conversions (for example if your value type is a wrapper around an Integer, then you can still pass it wherever an Integer is expected by some already-written method )

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 Integers.


Solution

  • General

    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 Integers.

    But they cannot behave as if they were Integers 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,

    Side note: @Value

    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.

    Side note: Bean Validation API

    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;