Wednesday, 15 October 2025

Type conversion in Java - an alternative proposal for primitive type patterns

A lot of good work has been done by the core Java team on patterns, providing new ways to explore data. The latest extension, in JEP 507, is the idea that primitive type patterns should be supported. Today I'm publishing an alternative approach.

Primitive Types in Patterns, instanceof, and switch

The current proposal is as follows:

  long val = createLong();
  int i = (int) val;  // cast long to int, potentially silently losing information
  switch (val) {
    case int j -> IO.println("Long fits in an int");
    case long v -> IO.println("Long does not fit in an int");
  };

I like the idea of being able to tell if a long value fits into an int without loss. But I hate the syntax.

The key problem is that type patterns check the supertype/subtype relationship, and int is not a subtype of long. The result is code that doesn't seem to make sense.

The official explanation is based on the notion that develoeprs use instanceof String before a cast to String all the time. Thus a parallel can be drawn to have an instanceof int before a cast to int. Effectively the aim is to extend the meaning of type patterns to cover primitive type casts, which are type conversions, not type checks.

I know I am not alone in finding this argument weak, and in finding the proposed syntax highly confusing. But it took a while, and an 8 page document, to figure out exactly why.

Type conversion in Java

In response to the JEP and subsequent discussions, I have written up a detailed proposal for type conversion casts and type conversion patterns. These allow developers to more clearly express the difference between type checks (that check the supertype/subtype relationship) and type conversions (where primitive types are changed to a different type).

The big idea is to introduce a new kind of cast, the type conversion cast that operates like a standard primitive type cast, but throws an exception when the conversion would be lossy.

  long val = createLong();
  int i = (int) val;   // cast long to int, potentially losing information by truncation
  int j = (~int) val;  // cast long to int, throwing TypeConversionException if lossy

As can be seen, the new kind of cast blends in well, but immediately offers safety benefits. Millions of primitive type casts in Java applications are intended to be fully safe, but are unchecked and could silently lose information. Simply adding one extra character ~ would upgrade them to a safer alternative.

The related type conversion pattern allows switch and instanceof to safely check for type conversions:

  long val = createLong();
  switch (val) {
    case ~int j -> IO.println("Long fits in an int");
    case long v -> IO.println("Long does not fit in an int");
  };

This allows pattern matching in switch for primitive types, but highlights the different things the pattern is checking.

  • Type patterns check the supertype/subtype relationship.
  • Type conversion patterns check point-to-point conversions, defined for primitive types or value types.

Please read the full proposal and provide feedback.

No comments:

Post a Comment

Please be aware that by commenting you provide consent to associate your selected profile with your comment. Long comments or those with excessive links may be deleted by Blogger (not me!). All spam will be deleted.