Search code examples
javacompiler-errorsautoboxingjls

JLS violations via Number and custom types for switch selector expression


JLS 17 mentions for the switch selector expression (like the JLS' before it)

The type of the selector expression must be char, byte, short, int, Character, Byte, Short, Integer, String, or an enum type (§8.9), or a compile-time error occurs.

Yet, this compiles (and works)

public class rough2 {
    class f{
        public static void sop(Object o){System.out.println(o);}
    }
    public static void main(String[] args) {
        Number no = 10.23f;
        switch (no) {
            case Integer i -> f.sop("%d is integer".formatted(i));
            case Float fl -> f.sop("%3.2f is float".formatted(fl));
            default -> f.sop("don't know about "+no);
        }
    }
}

Now, Number isn't one of the listed types.

  1. Why is there no compilation error?

Interestingly, no.getClass() gives java.lang.Float. Is generic autoboxing a thing?

Even more bizarrely (and maybe this should be a separate question - if so tell me), replacing the above main method with the one below still works.

public static void main(String[] args) {
        class a {}
        class b extends a {}
        a obj = new a();//new b();
        switch (obj) {
            case b typeB -> f.sop("is sub type b");
            case a typeA -> f.sop("is a");
        }
    }
  1. How is this not a JLS violation?

Please note that you may need to --enable-preview features in JDK 17 to run the above code.


Solution

  • The specific syntax you show is defined in JEP 406: Pattern Matching for switch (Preview), and is a preview feature. Preview features are not defined in the JLS itself, but instead are defined separately, as mentioned in JSL 17 section 1.5. Preview Features:

    Preview language features are specified in standalone documents that indicate changes ("diffs") to The Java® Language Specification for that release. The specifications of preview language features are incorporated into The Java® Language Specification by reference, and made a part thereof, if and only if preview features are enabled at compile time.

    Java SE 17 defines one preview language feature: Pattern Matching for switch. The standalone document which specifies this preview feature is available at the Oracle web site which hosts The Java® Language Specification: https://docs.oracle.com/javase/specs/.

    This 'diff' document of JLS 17 for the preview feature is Pattern Matching for switch. It 'applies' when you specify --enable-preview. Among others, it removes the line you quote in your question:

    The type of the selector expression must be char, byte, short, int, Character, Byte, Short, Integer, String, or an enum type (8.9), or a compile-time error occurs.

    As to your question "Why is there no compilation error?", because that is how this feature works. You provide a Number, and then check whether it is an Integer or a Float, or otherwise apply a default, both Integer and Float are subclasses of Number. I recommend reading JEP 406 for more details.

    As to "Is generic autoboxing a thing?", there are no generics involved here. You assign a float literal to a variable of an object type, so it is autoboxed to Float, and given Number is a superclass of Float, the assignment is valid.

    I'm not sure what you mean with the "Even more bizarrely" part, or why you think it is bizarre, but again: read JEP 406 for more details.