Java8: почему запрещается определять метод по умолчанию для метода из java.lang.Object

Способы по умолчанию – прекрасный новый инструмент в нашей панели инструментов Java. Однако я попытался написать интерфейс, который определяет версию метода toString по default . Java говорит мне, что это запрещено, поскольку методы, объявленные в java.lang.Object не могут быть default . Почему это так?

Я знаю, что правило «базовый class всегда выигрывает», поэтому по умолчанию (pun;) любая реализация Object default будет в любом случае перезаписываться методом Object . Однако я не вижу причин, по которым не должно быть исключения для методов из Object в спецификации. Специально для toString было бы очень полезно иметь реализацию по умолчанию.

Итак, в чем причина, почему разработчики Java решили не разрешать методы по default переопределяющие методы из Object ?

Это еще одна из тех проблем, связанных с языковым дизайном, которая кажется «явно хорошей идеей», пока вы не начнете копать, и вы понимаете, что на самом деле это плохая идея.

У этой почты много вопросов по этому вопросу (и по другим предметам тоже.) Было несколько проектных сил, которые сходились, чтобы привести нас к нынешнему дизайну:

  • Желание сохранить модель наследования простой;
  • Тот факт, что когда вы просматриваете очевидные примеры (например, превращая AbstractList в интерфейс), вы понимаете, что наследование equals / hashCode / toString сильно привязано к единому наследованию и состоянию, а интерфейсы многократно унаследованы и не имеют состояния;
  • То, что это потенциально открыло дверь к некоторым неожиданным поведением.

Вы уже коснулись цели «держать ее простой»; правила наследования и разрешения конфликтов разработаны очень просто (classы перехватывают интерфейсы, производные интерфейсы выигрывают над суперинтерфейсами, а любые другие конфликты разрешаются classом реализации.) Конечно, эти правила могут быть изменены, чтобы сделать исключение, но Я думаю, вы обнаружите, что когда начнете тянуть эту строку, инкрементная сложность не так мала, как вы думаете.

Конечно, есть определенная степень пользы, которая оправдывала бы большую сложность, но в этом случае ее там нет. Методы, о которых мы говорим, это equals, hashCode и toString. Эти методы суть внутреннее состояние объекта, и именно classу принадлежит государство, а не интерфейс, который находится в лучшем положении для определения того, что означает равенство для этого classа (особенно, поскольку контракт на равенство довольно силен, см. Эффективный Java для некоторых неожиданных последствий); интерфейсные писатели слишком далеки.

Легко вытащить пример AbstractList ; было бы прекрасно, если бы мы могли избавиться от AbstractList и поместить поведение в интерфейс List . Но как только вы выйдете за пределы этого очевидного примера, не так много других хороших примеров. В root, AbstractList предназначен для одиночного наследования. Но интерфейсы должны быть разработаны для множественного наследования.

Далее, представьте, что вы пишете этот class:

 class Foo implements com.libraryA.Bar, com.libraryB.Moo { // Implementation of Foo, that does NOT override equals } 

Автор Foo смотрит на супертипы, не видит реализации равных и делает вывод, что для получения ссылочного равенства все, что ему нужно, это наследовать равные от Object . Затем, на следующей неделе, поддерживающий библиотеку для Bar «помогает» добавляет стандартную equals реализацию. По электронной почте Ой! Теперь семантика Foo была нарушена интерфейсом в другом домене обслуживания «с пользой», добавив значение по умолчанию для общего метода.

Предполагаются значения по умолчанию. Добавление значения по умолчанию к интерфейсу, где его не было (нигде в иерархии), не должно влиять на семантику конкретных classов реализации. Но если параметры по умолчанию могут «переопределять» методы объекта, это не так.

Таким образом, хотя это кажется безобидной особенностью, это на самом деле довольно вредно: это добавляет много сложности для небольшой инкрементной выразительности, и это слишком легко для благих намерений, безвредных изменений для отдельно скомпилированных интерфейсов для подрыва предполагаемая семантика реализующих classов.

Запрещается определять методы по умолчанию в интерфейсах для методов в java.lang.Object , поскольку методы по умолчанию никогда не будут «достижимыми».

Методы интерфейса по умолчанию могут быть перезаписаны в classах, реализующих интерфейс, и реализация classа метода имеет более высокий приоритет, чем реализация интерфейса, даже если этот метод реализован в суперclassе. Поскольку все classы наследуются от java.lang.Object , методы в java.lang.Object имеют приоритет над методом по умолчанию в интерфейсе и вместо этого будут вызваны.

Брайан Гетц из Oracle предоставляет несколько подробностей о дизайнерском решении в этом списке рассылки .

Я не вижу в голове авторов языка Java, поэтому мы можем только догадываться. Но я вижу много причин и соглашаюсь с ними абсолютно в этом вопросе.

Основной причиной внедрения методов по умолчанию является возможность добавления новых методов в интерфейсы без нарушения обратной совместимости более старых реализаций. Методы по умолчанию также могут использоваться для обеспечения «удобных» методов без необходимости их определения в каждом из classов реализации.

Ни одно из них не относится к toString и другим методам Object. Проще говоря, методы по умолчанию были разработаны для обеспечения поведения по умолчанию, когда другого определения нет. Не предоставлять реализаций, которые будут «конкурировать» с другими существующими реализациями.

Правило «базовый class всегда выигрывает» имеет и свои веские причины. Предполагается, что classы определяют реальные реализации, а интерфейсы определяют реализации по умолчанию , которые несколько слабее.

Кроме того, введение ЛЮБОГО исключения из общих правил вызывают ненужную сложность и поднимают другие вопросы. Объект (более или менее) является classом, как и любой другой, так почему у него должно быть другое поведение?

Все и все, решение, которое вы предлагаете, вероятно, принесет больше минусов, чем профи.

Обоснование очень просто, потому что Object является базовым classом для всех classов Java. Поэтому, даже если у нас есть метод Object, определенный как метод по умолчанию в каком-либо интерфейсе, он будет бесполезен, поскольку метод Object всегда будет использоваться. Поэтому, чтобы избежать путаницы, мы не можем использовать методы по умолчанию, которые переопределяют методы classа Object.

Чтобы дать очень педантичный ответ, запрещается определять метод по default для общедоступного метода из java.lang.Object . Существует 11 методов, которые можно отнести к трем способам ответа на этот вопрос.

  1. Шесть методов Object не могут иметь методы по default потому что они являются final и не могут быть переопределены вообще: getClass() notify() , notifyAll() , notifyAll() , wait() , wait(long) и wait(long, int) .
  2. Три метода Object не могут использовать методы по default приведенные выше Брайаном Гетцем: equals(Object) , hashCode() и toString() .
  3. Два метода Object могут иметь методы по default , хотя значение таких значений по умолчанию в лучшем случае сомнительно: clone() и finalize() .

     public class Main { public static void main(String... args) { new FOO().clone(); new FOO().finalize(); } interface ClonerFinalizer { default Object clone() {System.out.println("default clone"); return this;} default void finalize() {System.out.println("default finalize");} } static class FOO implements ClonerFinalizer { @Override public Object clone() { return ClonerFinalizer.super.clone(); } @Override public void finalize() { ClonerFinalizer.super.finalize(); } } } 
  • Как один интерфейс может использоваться для различных фоновых задач андроида?
  • Как написать тесты junit для интерфейсов?
  • Зачем явно реализовывать интерфейс?
  • Методология интерфейсов Java: должен ли каждый class реализовывать интерфейс?
  • Реализует vs extends: Когда использовать? Какая разница?
  • Объяснение интерфейсов для учащихся
  • Защищено в интерфейсах
  • Java generics - сделать Generic для расширения двух интерфейсов
  • Почему по умолчанию переменные интерфейса статичны и окончательны?
  • Являются ли значения по умолчанию в JDK 8 формой множественного наследования в Java?
  • Интерфейсы наследуются от classа Object в java
  • Давайте будем гением компьютера.