Ковариантные возвращаемые типы в перечислениях Java

Как уже упоминалось в другом вопросе на этом сайте, что-то подобное не является законным:

public enum MyEnum {
    FOO {
        public Integer doSomething() { return (Integer) super.doSomething(); }
    },
    BAR {
        public String doSomething() { return (String) super.doSomething(); }
    };

    public Object doSomething();
}

Это происходит из-за того, что ковариантные типы возвращаемых данных, очевидно, не работают с константами перечисления (снова разрушая иллюзию, что константы перечисления являются одноэлементными подклассами типа перечисления...) Итак, как насчет того, чтобы добавить немного обобщений: это допустимо?

public enum MyEnum2 {
    FOO {
        public Class<Integer> doSomething() { return Integer.class; }
    },
    BAR {
        public Class<String> doSomething() { return String.class; }
    };

    public Class<?> doSomething();
}

Здесь все три возвращаются Class объекты, но отдельные константы "более специфичны", чем тип enum в целом...

3 ответа

Решение

По сути, проблема в том, что тип времени компиляции MyEnum.FOO является MyEnum, а не конкретный сгенерированный подкласс. Вы можете увидеть это без какой-либо ковариации:

enum MyEnum {
    FOO {
        public void foo() {}
    };
}

public class Test {
    public static void main(String[] args) throws Exception
    {
        MyEnum.FOO.foo(); // Error
    }
}

По сути, компилятор будет видеть только подписи, объявленные в MyEnum,

Мы можем использовать возвращаемые типы Covariant в перечислениях Java, но мы не можем использовать этот дочерний тип для переменной, которой назначен возврат, в отличие от обычной иерархии классов.

public class EnumCovariance {

   public static void main(String[] args) {
      // Type mismatch: cannot convert from Class<capture#1-of ?> to Class<Integer>
      Class<Integer> something = MyEnum2.FOO.doSomething();
      Child child = new Child();
      Base base = child;
      // ok
      Class<Integer> something3 = child.doSomething();

      // Type mismatch: cannot convert from Class<capture#2-of ?> to Class<Integer>
      Class<Integer> something2 = base.doSomething();
    }
}

abstract class Base {
   public abstract Class<?> doSomething();
}

class Child extends Base {
   @Override
   public Class<Integer> doSomething() {
      return Integer.class;
   }
}

enum MyEnum2 {
   FOO {
      public Class<Integer> doSomething() {
         return Integer.class;
      }
   },
   BAR {
      public Class<String> doSomething() {
         return String.class;
      }
   };

   public abstract Class<?> doSomething();
}

Этот код хорошо работает:

public enum MyEnum {
   FOO {
      @Override
      public Integer doSomething() {
         return (Integer)super.doSomething();
      }
   },
   BAR {
      @Override
      public String doSomething() {
         return (String)super.doSomething();
      }
   };

   public Object doSomething() {
      System.err.println( this );
      return null;
   }

   public static void main( String[] args ) {
      MyEnum toto = MyEnum.FOO;
      System.err.println( toto.doSomething() );
   }
}

вывод:

FOO
null

этот код не компилируется, ошибка "The return type is incompatible with MyEnum2.doSomething()", Cannot cast from Class<Object> to Class<Integer> а также Cannot cast from Class<Object> to Class<String>:

public enum MyEnum2 {
   FOO {
      @Override
      public Class<Integer> doSomething() {
         return (Class<Integer>)super.doSomething();
      }
   },
   BAR {
      @Override
      public Class<String> doSomething() {
         return (Class<String>)super.doSomething();
      }
   };

   public Class<Object> doSomething() {
      System.err.println( this );
      return null;
   }

   public static void main( String[] args ) {
      MyEnum2 toto = MyEnum2.FOO;
      System.err.println( toto.doSomething() );
   }
}
Другие вопросы по тегам