Подтвердить что ты не робот

Java 8 нуждается в отливке, в то время как Java 7 - нет - enum.getClass/getDeclaringClass

Я понимаю, что Java 8 все еще находится в бета-версии, но это показалось мне нечетным:

public class Fields<C extends Enum<C>> {

    public Fields(Set<C> columns) {
        // A sample column used to find the universe of the enum of Columns.
        C sampleCol = columns.iterator().next();
        // Java 8 needs a cast here.
        Set<C> allColumns = EnumSet.allOf((/*Class<C>)*/ sampleCol.getClass());
        // ... there more to this that I've deleted.
    }

}

Ошибка:

error: incompatible types: inferred type does not conform to equality constraint(s)
            Set<C> allColumns = EnumSet.allOf(sampleCol.getClass());
    inferred: C
    equality constraints(s): C,CAP#1
  where C is a type-variable:
    C extends Enum<C> declared in class Test.Fields
  where CAP#1 is a fresh type-variable:
    CAP#1 extends Enum from capture of ? extends Enum

Является ли это ошибкой или новой функцией Java 8?

4b9b3361

Ответ 1

Интересно, это тонкое изменение в обработке необработанных типов.

Во-первых, давайте проясним ваш пример. Тип возврата Object.getClass является особенным:

Фактический тип результата Class<? extends |X|>, где |X| - это стирание статического типа выражения, на которое вызывается getClass.

В этом случае X будет параметром типа C, который стирает до Enum. Итак sampleCol.getClass() возвращает Class<? extends Enum>. EnumSet.allOf объявляет параметр типа E extends Enum<E>, а в вашем случае ? extends Enum выводится как его аргумент типа.

Важная часть состоит в том, что Enum является сырым типом. Было замечено, что использование типов raw стирает, по-видимому, несвязанные дженерики, например, на этом посту: Почему этот общий код Java не компилируется? В своем ответе там Джон Skeet цитирует JLS §4.8 ( "Необработанные типы" ), чтобы покрыть это неинтуитивное поведение.

Подобное поведение, по-видимому, происходит в вашем примере с Java 7: EnumSet.allOf(sampleCol.getClass()) разрешено компилировать с предупреждением "unchecked invocation" (это скрывается при последующем предупреждении "непроверенного преобразования" о назначении полученного raw EnumSet до Set<C>).

Возникает вопрос: должно ли возникновение необработанного типа в общих подстановочных границах разрешать непроверенные преобразования? JLS §4.8 не упоминает об этом, поэтому он неоднозначен. Возможно, это ошибка, но это похоже на разумное ужесточение этого поведения. Хотя стандартного типа raw, такого как Enum, можно было бы ожидать от устаревших API, "наполовину испеченный" тип, такой как Class<? extends Enum>, мог появляться только после генерики, и поэтому на самом деле не имеет смысла позволять ему нарушать проверку общего типа.

В любом случае мне интересно узнать, может ли кто-нибудь указать на документацию об этом изменении - мой поиск ничего не изменил.


О вашем конкретном коде: вы должны использовать getDeclaringClass(). Компилятор не может знать, что вызов getClass на C будет возвращать точно Class<C>; на самом деле, он не будет использоваться в перечислении с классом постоянной определенности. Это именно тот прецедент, для которого Enum объявляет этот метод.