Ошибка Java 8: наследование интерфейса Аннотация и значение по умолчанию

Я пытаюсь написать библиотеку интерфейсов коллекции, которые реализуют большинство методов в стандартном API Collection, используя новый синтаксис метода по умолчанию в Java 8. Вот небольшой пример того, что я собираюсь сделать:

public interface MyCollection<E> extends Collection<E> {
    @Override default boolean isEmpty() {
        return !iterator().hasNext();
    }
    //provide more default overrides below...
}

public interface MyList<E> extends MyCollection<E>, List<E> {
    @Override default Iterator<E>iterator(){
        return listIterator();
    }
    //provide more list-specific default overrides below...
}

Однако даже этот простой пример встречается с ошибкой компилятора:

error: interface MyList<E> inherits abstract and default
       for isEmpty() from types MyCollection and List

Из моего понимания методов по умолчанию это должно быть разрешено, поскольку только один из расширенных интерфейсов обеспечивает реализацию по умолчанию, но, по-видимому, это не так. Что здесь происходит? Есть ли способ заставить это делать то, что я хочу?

Ответ 1

Это объясняется в разделе раздел 9.4.1.3 (Наследование методов с переопределяющими эквивалентными сигнатурами) Спецификации языка Java:

Интерфейс может наследовать несколько методов с переопределяющими эквивалентными сигнатурами (§8.4.2).

...

Аналогично, когда абстрактный метод и метод по умолчанию с соответствующими сигнатурами наследуются, мы создаем ошибку. В этом случае можно было бы отдать приоритет одному или другому - возможно, мы предположили бы, что метод по умолчанию обеспечивает разумную реализацию абстрактного метода. Но это рискованно, потому что, помимо имени совпадения и подписи, у нас нет оснований полагать, что метод по умолчанию ведет себя последовательно с контрактом абстрактного метода - метод по умолчанию, возможно, даже не существовал, когда исходный интерфейс был первоначально разработан. В этой ситуации безопаснее просить пользователя активно утверждать, что реализация по умолчанию является подходящей (через переопределяющее объявление).

Итак, поскольку оба MyCollection и List определяют метод isEmpty(), а один - по умолчанию, а другой абстрактный, компилятор требует, чтобы подпоследователь явно указывал, какой из них он должен наследовать, переопределив метод еще раз. Если вы хотите, чтобы метод по умолчанию MyCollection был унаследован, вы можете вызвать его в переопределяющей реализации:

public interface MyList<E> extends MyCollection<E>, List<E> {
    @Override default boolean isEmpty() {
        return MyCollection.super.isEmpty();
    }

    @Override default Iterator<E> iterator(){
        return listIterator();
    }
    ...
}

Если вы хотите, чтобы MyList сохранял тег isEmpty() (который, как я думаю, вы не хотите), вы можете сделать:

public interface MyList<E> extends MyCollection<E>, List<E> {
    @Override boolean isEmpty();

    @Override default Iterator<E> iterator(){
        return listIterator();
    }
    ...
}

Ответ 2

измените исходный код на

public interface MyList<E> extends MyCollection<E>,List<E> {
     @Override 
     default boolean isEmpty(){
           return MyCollection.super.isEmpty();
      }
}

Для получения дополнительной информации перейдите по ссылке, реализация по умолчанию в интерфейсе