2

The following code compiles:

    public enum Foo {
        A,
        B{};
        public static void main(String[] args) {
            Foo f = Foo.A;
            List s = (List)f;
        }
    }

This one doesn't:

public enum Foo {
        A,
        B;
        public static void main(String[] args) {
            Foo f = Foo.A;
            List s = (List)f;
        }
    }

I could also replace Foo.A with Foo.B and get the same result. What is going on here? How could Foo.A ever be a List in the first example?

Hans Wurst
  • 376
  • 1
  • 3
  • 12
  • 2
    I guess the compiler reasons that since your enum does have subtypes, that it could have a subtype that implements the interface; even though it actually does not. – khelwood Oct 14 '20 at 14:37
  • I tried to compile your first example and it doesn't work (made enum non-public and moved `main` to separate public class tho). Edit. it compiles but throws `ClassCastException` during execution. – Amongalen Oct 14 '20 at 14:39
  • 2
    @Amongalen yes that's the point. There will always be a `ClassCastException` and I do not understand why the compiler wouldn't know :) – Hans Wurst Oct 14 '20 at 14:46
  • @khelwood as far as I know `enum` in Java cannot inherit from another type. – Hans Wurst Oct 14 '20 at 14:48
  • @HansWurst Actually, I think enums can implement interfaces, just not extend classes since they already extend `Enum` – user Oct 14 '20 at 14:50
  • @user yes you can but `Foo` actually doesn't and Java does not allow "subenums" so there is no type that is a `Foo` and implements `List` – Hans Wurst Oct 14 '20 at 14:52
  • @HansWurst Yeah, you are right about that. Perhaps all the Java compiler checks is that there are subclasses of `Foo` and doesn't bother checking if any of them actually implement `List` – user Oct 14 '20 at 14:53

1 Answers1

3

For this type of casting, the spec on Narrowing Reference Conversion defines the rules. There is no special case for enums, only a distinction between final and non-final classes.

The basic enum falls into the "final class" category, but your extended enum doesn't, as it introduces a subclass via the {} syntax.

Of course, even with the extended enum, there's no way that one of your enum constants could ever implement List, but the current spec simply doesn't cover that situation.

A future revision of the spec might improve that, and then I'd expect compilers to implement the additional checks. But right now, that degree of compile-time safety isn't available.

Ralf Kleberhoff
  • 6,990
  • 1
  • 13
  • 7
  • Mhh that is surprising as `javap Foo.class` gives me `public class Foo extends java.lang.Enum { ... }` for both. – Hans Wurst Oct 14 '20 at 18:05
  • @HansWurst I guess someone can add an interface to that enum, _at runtime_. – Eugene Oct 14 '20 at 20:13
  • 2
    @HansWurst [there you go](https://stackoverflow.com/a/32124809/2711488)… – Holger Oct 21 '20 at 18:11
  • 2
    @Eugene it’s possible (see my previous comment), but I don’t think that the Java language designers considered this possibility when defining the rules. It just has not been considered to add a special rule for `enum` types, so the combination of existing rules leads to the behavior we’re seeing here. – Holger Oct 21 '20 at 18:20