Методы по умолчанию - отличный новый инструмент в нашем наборе инструментов Java. Однако я попытался написать интерфейс, определяющий default
версию toString
метода. Java сообщает мне, что это запрещено, поскольку методы, объявленные в, java.lang.Object
нельзя default
редактировать. Почему это так?
Я знаю, что существует правило «базовый класс всегда побеждает», поэтому по умолчанию (каламбур;) любая default
реализация Object
метода в Object
любом случае будет перезаписана этим методом . Однако я не вижу причин, по которым не должно быть исключения для методов из Object
спецификации. Особенно для toString
этого может быть очень полезно иметь реализацию по умолчанию.
Итак, в чем причина, по которой разработчики Java решили не разрешать default
методы, замещающие методы Object
?
источник
Ответы:
Это еще одна проблема языкового дизайна, которая кажется «очевидно хорошей идеей», пока вы не начнете копать и не поймете, что это на самом деле плохая идея.
В этом письме много на эту тему (и на другие темы тоже). Было несколько дизайнерских сил, которые объединились, чтобы привести нас к текущему дизайну:
AbstractList
в интерфейс), вы поймете, что наследование equals / hashCode / toString сильно привязано к одиночному наследованию и состоянию, а интерфейсы наследуются многократно и не имеют состояния;Вы уже затронули цель «сохранить простоту»; правила наследования и разрешения конфликтов спроектированы так, чтобы быть очень простыми (классы имеют приоритет над интерфейсами, производные интерфейсы преобладают над суперинтерфейсами, и любые другие конфликты разрешаются реализующим классом). Конечно, эти правила можно настроить, чтобы сделать исключение, но Я думаю, вы обнаружите, когда начнете тянуть за эту веревку, что возрастающая сложность не так мала, как вы думаете.
Конечно, есть определенные преимущества, которые оправдывают большую сложность, но в данном случае их нет. Здесь мы говорим о методах equals, hashCode и toString. Все эти методы по сути связаны с состоянием объекта, и именно класс, которому принадлежит состояние, а не интерфейс, находится в лучшем положении, чтобы определить, что равенство означает для этого класса (тем более, что контракт на равенство довольно сильный; см. Java для некоторых удивительных последствий); писатели интерфейсов слишком далеко ушли.
AbstractList
Пример легко вытащить ; было бы прекрасно, если бы мы могли избавиться отAbstractList
поведения и перенести его вList
интерфейс. Но как только вы отойдете от этого очевидного примера, вы не сможете найти много других хороших примеров. В корневом каталогеAbstractList
предназначен для одиночного наследования. Но интерфейсы должны быть предназначены для множественного наследования.Далее представьте, что вы пишете этот класс:
В
Foo
писатель смотрит на супертипах, не видит реализации равных, и приходят к выводу , что , чтобы получить равенство ссылок, все , что ему нужно сделать , это наследовать равно отObject
. Затем, на следующей неделе, сопровождающий библиотеки Bar «услужливо» добавляетequals
реализацию по умолчанию . По электронной почте Ой! Теперь семантикаFoo
была нарушена интерфейсом в другом домене обслуживания «услужливо» добавлением значения по умолчанию для общего метода.Значения по умолчанию должны быть значениями по умолчанию. Добавление значения по умолчанию к интерфейсу там, где его не было (где-либо в иерархии), не должно влиять на семантику конкретных реализующих классов. Но если бы значения по умолчанию могли «переопределить» методы объекта, это было бы неправдой.
Таким образом, хотя это кажется безобидной функцией, на самом деле она довольно вредна: она добавляет много сложности для небольшой дополнительной выразительности и делает слишком легким для благих намерений, безобидно выглядящих изменений отдельно скомпилированные интерфейсы, чтобы подорвать предполагаемая семантика реализации классов.
источник
hashCode
иequals
, но я думаю, что это было бы очень полезно дляtoString
. Например, какой-тоDisplayable
интерфейс может определятьString display()
метод, и это сэкономит массу шаблонов, чтобы его можно было определитьdefault String toString() { return display(); }
вDisplayable
, вместо того, чтобы требовать от каждогоDisplayable
реализоватьtoString()
или расширитьDisplayableToString
базовый класс.toString()
он основан только на методах интерфейса, вы можете просто добавить что-то вродеdefault String toStringImpl()
интерфейса и переопределитьtoString()
в каждом подклассе для вызова реализации интерфейса - немного некрасиво, но работает и лучше, чем ничего. :) Другой способ сделать это - сделать что-то вродеObjects.hash()
,Arrays.deepEquals()
иArrays.deepToString()
. +1 за ответ @BrianGoetz!default toString()
переопределения в функциональном интерфейсе позволило бы нам - по крайней мере - сделать что-то вроде выдачи сигнатуры функции и родительского класса разработчика. Еще лучше, если бы мы могли применить некоторые рекурсивные стратегии toString, мы могли бы пройти через закрытие, чтобы получить действительно хорошее описание лямбда и, таким образом, значительно улучшить кривую обучения лямбда.Запрещено определять методы по умолчанию в интерфейсах для методов в
java.lang.Object
, поскольку методы по умолчанию никогда не будут «достижимыми».Методы интерфейса по умолчанию могут быть перезаписаны в классах, реализующих интерфейс, и реализация метода класса имеет более высокий приоритет, чем реализация интерфейса, даже если метод реализован в суперклассе. Поскольку все классы наследуются от
java.lang.Object
, методы вjava.lang.Object
интерфейсе будут иметь приоритет перед методом по умолчанию и будут вызываться вместо этого.Брайан Гетц из Oracle предоставляет еще несколько подробностей о дизайнерском решении в этом сообщении списка рассылки .
источник
Я не вникаю в головы авторов языка Java, поэтому можно только догадываться. Но я вижу много причин и полностью согласен с ними в этом вопросе.
Основная причина введения методов по умолчанию - это возможность добавлять новые методы в интерфейсы без нарушения обратной совместимости старых реализаций. Методы по умолчанию также могут использоваться для предоставления «удобных» методов без необходимости определять их в каждом из реализующих классов.
Ничего из этого не применимо к toString и другим методам Object. Проще говоря, методы по умолчанию были разработаны для обеспечения поведения по умолчанию там, где нет другого определения. Не предоставлять реализации, которые будут «конкурировать» с другими существующими реализациями.
Правило «базовый класс всегда побеждает» также имеет свои веские причины. Предполагается, что классы определяют реальные реализации, а интерфейсы определяют реализации по умолчанию , которые несколько слабее.
Кроме того, введение ЛЮБЫХ исключений из общих правил вызывает ненужные сложности и вызывает другие вопросы. Объект (более или менее) является классом, как и любой другой, так почему он должен вести себя иначе?
В общем, предлагаемое вами решение принесет больше минусов, чем плюсов.
источник
Рассуждения очень просты, потому что Object является базовым классом для всех классов Java. Таким образом, даже если у нас есть метод объекта, определенный как метод по умолчанию в каком-либо интерфейсе, он будет бесполезен, потому что метод объекта всегда будет использоваться. Вот почему, чтобы избежать путаницы, у нас не может быть методов по умолчанию, которые переопределяют методы класса Object.
источник
Чтобы дать очень педантичный ответ, запрещено определять
default
метод только для общедоступного метода изjava.lang.Object
. Необходимо рассмотреть 11 методов, которые можно разделить на три категории, чтобы ответить на этот вопрос.Object
методов не может иметьdefault
методы , потому что ониfinal
и не могут быть переопределены на всех:getClass()
,notify()
,notifyAll()
,wait()
,wait(long)
, иwait(long, int)
.Object
методов не может иметьdefault
методы по причинам , указанным выше Брайан Гетц:equals(Object)
,hashCode()
, иtoString()
.Два
Object
метода могут иметьdefault
методы, хотя ценность таких значений по умолчанию в лучшем случае сомнительна:clone()
иfinalize()
.источник