Почему <T extends Enum <T> и SomeInterface> компилируется, но не <T extends SomeInterface & Enum <T>>?

Я не могу понять, почему метод2 не компилируется, тогда как метод1 компилируется. Я использую Eclipse с JavaSE 1.7, и я получил следующую ошибку в методе2:

Несколько маркеров в этой строке

  • Тип Enum <T> не является интерфейсом; он не может быть указан как ограниченный параметр

  • Связанное несоответствие: тип T не является допустимым заменителем ограниченного параметра < E расширяет Enum < E → типа Enum <E>

public class Test {

    public interface SomeInterface {

    }

    public static <T extends Enum<T> & SomeInterface> T method1() {
        return null;
    }

    public static <T extends SomeInterface & Enum<T>> T method2() {
        return null;
    }
}

Ответ 1

Если вы посмотрите на синтаксис для ограничений параметров типа в JLS 8.1.2, вы увидите:

TypeBound:
    extends TypeVariable 
    extends ClassOrInterfaceType {AdditionalBound}

AdditionalBound:
    & InterfaceType

Другими словами, только указанный первый тип может быть классом - все остальные должны быть интерфейсами.

Помимо всего прочего, это предотвращает указание нескольких классов.

Это также отражает то, как при объявлении класса вам нужно сначала поставить класс, который он расширяет, а затем интерфейсы, которые он реализует, а не наоборот.

Ответ 2

В соответствии с docs:

Переменная типа с несколькими ограничениями является подтипом всех типов, перечисленных в оценке. Если одна из границ является классом, она должна быть указана первыми. Например:

Class A { /* ... */ }
interface B { /* ... */ }
interface C { /* ... */ }

class D <T extends A & B & C> { /* ... */ }

Если привязка A сначала не указана, вы получаете ошибку времени компиляции:

class D <T extends B & A & C> { /* ... */ }  // compile-time error

Так как в вашем примере method2 сначала имеет интерфейс SomeInterface, он показывает ошибку компилятора