Одной из наиболее полезных функций Java 8 являются новые default
методы интерфейсов. По сути, есть две причины (могут быть и другие), почему они были введены:
- Предоставление фактических реализаций по умолчанию. Пример:
Iterator.remove()
- С учетом эволюции JDK API. Пример:
Iterable.forEach()
С точки зрения разработчика API, мне бы хотелось иметь возможность использовать другие модификаторы в методах интерфейса, например final
. Это было бы полезно при добавлении вспомогательных методов, предотвращающих «случайные» переопределения при реализации классов:
interface Sender {
// Convenience method to send an empty message
default final void send() {
send(null);
}
// Implementations should only implement this method
void send(String message);
}
Вышесказанное уже является обычной практикой, если бы Sender
был класс:
abstract class Sender {
// Convenience method to send an empty message
final void send() {
send(null);
}
// Implementations should only implement this method
abstract void send(String message);
}
Теперь, default
и final
они явно противоречат ключевым словам, но само ключевое слово по умолчанию не было бы строго обязательным , поэтому я предполагаю, что это противоречие является преднамеренным, чтобы отразить тонкие различия между «методами класса с телом» (только методы) и «интерфейсом». "методы с телом" (методы по умолчанию), т.е. различия, которые я еще не понял.
В какой-то момент поддержка модификаторов, таких как static
и final
в интерфейсных методах, еще не была полностью изучена, ссылаясь на Брайана Гетца :
Другая часть заключается в том, как далеко мы пойдем, чтобы поддерживать инструменты построения классов в интерфейсах, такие как конечные методы, частные методы, защищенные методы, статические методы и т. Д. Ответ: мы еще не знаем
С тех пор, в конце 2011 года, очевидно, static
была добавлена поддержка методов в интерфейсах. Понятно, что это добавило много ценности самим библиотекам JDK, например, с помощью Comparator.comparing()
.
Вопрос:
По какой причине final
(а также static final
) никогда не доходило до интерфейсов Java 8?
источник
final
предотвращает переопределение метода, и, видя, как вы ДОЛЖНЫ переопределять методы, унаследованные от интерфейсов, я не понимаю, почему имеет смысл сделать его окончательным. Если только это не означало, что метод является окончательным ПОСЛЕ переопределения его один раз .. В таком случае, может быть, есть трудности? Если я не понимаю этого права, пожалуйста, дайте мне знать. Кажется интереснымfinal
можно было бы использовать предотвращение реализации классами переопределения реализации метода интерфейса по умолчанию.Ответы:
Этот вопрос в какой-то степени связан с тем, что является причиной, по которой «синхронизация» не допускается в интерфейсных методах Java 8?
Главное, что нужно понять о методах по умолчанию, заключается в том, что основная цель разработки - эволюция интерфейса , а не «превращение интерфейсов в (посредственные) черты». Хотя между ними есть некоторое совпадение, и мы пытались приспособиться к последнему, где это не мешало первому, эти вопросы лучше всего понять, если рассматривать их в этом свете. (Заметим также , что методы класса являются будет отличаться от методов интерфейса, независимо от того , что намерение, в силу того , что методы интерфейса можно умножить унаследовали.)
Основная идея метода по умолчанию: это интерфейсный метод с реализацией по умолчанию, а производный класс может предоставить более конкретную реализацию. И поскольку центр разработки был развитием интерфейса, то критической целью проекта было добавление методов по умолчанию к интерфейсам после факта в совместимом с исходным кодом и двоично-совместимом способе.
Слишком простой ответ на вопрос «почему не окончательные методы по умолчанию» заключается в том, что тогда тело будет не просто реализацией по умолчанию, а единственной реализацией. Хотя это слишком простой ответ, он дает нам подсказку, что вопрос уже движется в сомнительном направлении.
Другая причина, по которой окончательные методы интерфейса сомнительны, заключается в том, что они создают невозможные проблемы для разработчиков. Например, предположим, у вас есть:
Здесь все хорошо;
C
наследуетfoo()
отA
. Теперь предположим, чтоB
изменилсяfoo
метод, по умолчанию:Теперь, когда мы перейдем к перекомпиляции
C
, компилятор скажет нам, что он не знает, какое поведение наследоватьfoo()
, поэтомуC
должен переопределить его (и может выбрать делегирование,A.super.foo()
если он хочет сохранить то же поведение.) Но что еслиB
сделал свой дефолтfinal
, иA
не находится под контролем автораC
? СейчасC
безвозвратно сломан; он не может скомпилироваться без переопределенияfoo()
, но он не может переопределить,foo()
если он был последним вB
.Это всего лишь один пример, но суть в том, что окончательность для методов - это действительно инструмент, который имеет больше смысла в мире классов с единым наследованием (обычно связывающих состояние с поведением), чем с интерфейсами, которые просто вносят свой вклад в поведение и могут размножаться. по наследству. Слишком сложно рассуждать о том, «какие другие интерфейсы могут быть добавлены в возможный разработчик», и допущение, что интерфейсный метод будет окончательным, вероятно, вызовет эти проблемы (и они взорвутся не от человека, который написал интерфейс, а от бедный пользователь, который пытается это реализовать.)
Другая причина запретить им это то, что они не будут иметь в виду то, что вы думаете, они имеют в виду. Реализация по умолчанию рассматривается только в том случае, если класс (или его суперклассы) не предоставляют объявление (конкретное или абстрактное) метода. Если бы метод по умолчанию был финальным, но суперкласс уже реализовал метод, то по умолчанию игнорировался бы, что, вероятно, не то, что автор по умолчанию ожидал, объявив его финальным. (Такое поведение наследования является отражением центра разработки для методов по умолчанию - эволюция интерфейса. Должна быть возможность добавить метод по умолчанию (или реализацию по умолчанию к существующему методу интерфейса) к существующим интерфейсам, которые уже имеют реализации, без изменения поведение существующих классов, которые реализуют интерфейс,
источник
В лямбда-рассылке много дискуссий по этому поводу . Одна из тех, которые, кажется, содержат много дискуссий обо всем этом, заключается в следующем: Видимость метода интерфейса Varied (была Final defenders) .
В этой дискуссии Талден, автор оригинального вопроса, задает нечто очень похожее на ваш вопрос:
В конце концов ответ Брайана Гетца был:
Так что, скорее всего, он никогда не был реализован, потому что он никогда не был частью области видимости. Это никогда не было предложено вовремя, чтобы быть рассмотренным.
В другом бурном обсуждении окончательных методов защиты на эту тему Брайан снова сказал :
Так что это подтверждает мою теорию о том, что она просто не была частью объема или частью их дизайна. Что они сделали, так это предоставили достаточно функциональности для решения проблем эволюции API.
источник
Будет трудно найти и определить ответ «THE», потому что резонаторы, упомянутые в комментариях @EJP: в мире есть примерно 2 (+/- 2) человека, которые могут дать определенный ответ вообще . И в сомнении ответ мог бы быть что-то вроде: «Поддержка окончательных методов по умолчанию не стоила усилий по реструктуризации внутренних механизмов разрешения вызовов». Конечно, это предположение, но оно, по крайней мере, подкреплено неуловимыми доказательствами, такими как это заявление (одного из двух) в списке рассылки OpenJDK :
и тривиальные факты, как этот метод просто не считается (действительно) конечным методом, когда он является
default
методом, как в настоящее время реализовано в Method :: is_final_method в OpenJDK.Дальнейшую действительно «авторитетную» информацию действительно трудно найти, даже с чрезмерными поисками в сети и чтением журналов коммитов. Я подумал, что это может быть связано с потенциальной неоднозначностью при разрешении вызовов метода интерфейса с помощью
invokeinterface
инструкций и вызовов методов класса, соответствующихinvokevirtual
инструкции: Дляinvokevirtual
инструкции может быть простой поиск в vtable , поскольку метод должен быть либо наследуемым из суперкласса, или реализуется классом напрямую. В отличие от этого,invokeinterface
вызов должен изучить соответствующий сайт вызова, чтобы выяснить, к какому интерфейсу относится этот вызов (это объясняется более подробно в методах, которые либо не вставляются в виртуальную таблицу). странице InterfaceCalls в HotSpot Wiki). Тем не мение,final
или заменить существующие записи в vtable (см. klassVtable.cpp. Строка 333 ) и аналогичным образом методы по умолчанию заменяют существующие записи в vtable (см. klassVtable.cpp, Строка 202 ). Так актуально причина (и, следовательно, ответ) должна быть скрыта глубже в (довольно сложных) механизмах разрешения вызовов методов, но, возможно, эти ссылки, тем не менее, будут рассматриваться как полезные, будь то только для тех, кому удастся получить фактический ответ из этого.источник
int
s, но смотрите stackoverflow.com/questions/430346 ... ...synchronized
наdefault
методах.final
вопроса, и несколько унизительно, что никто больше не думал о подобных примерах (или, может быть, некоторые думали об этих примерах, но не чувствовали себя достаточно авторитетными, чтобы отвечать). Так что теперь (извините, я должен сделать это :) произнесено последнее слово.Я не думаю, что необходимо указывать
final
удобный метод интерфейса, хотя я могу согласиться, что он может быть полезным, но, по-видимому, затраты перевешивают преимущества.В любом случае, вы должны написать правильный javadoc для метода по умолчанию, точно показывая, что этот метод разрешен и запрещен. Таким образом, классы, реализующие интерфейс, «не допускаются» для изменения реализации, хотя нет никаких гарантий.
Любой может написать a,
Collection
который придерживается интерфейса, а затем делает вещи в методах, которые абсолютно противоречат интуитивному принципу, от этого нет никакого способа защитить себя, кроме как писать обширные модульные тесты.источник
final
было решено не разрешатьinterface
методы Java 8 . Недостаточное соотношение затрат и выгод является хорошим кандидатом, но пока это предположение.Мы добавляем
default
ключевое слово в наш метод внутри,interface
когда мы знаем, что класс, расширяющий,interface
может или не можетoverride
наша реализация. Но что, если мы хотим добавить метод, который мы не хотим, чтобы какой-либо реализующий класс переопределял? Ну, нам были доступны два варианта:default
final
метод.static
метод.Теперь Java говорит, что если у нас есть
class
реализация двух или болееinterfaces
таких, что у них естьdefault
метод с точно таким же именем и сигнатурой метода, т.е. они дублируют друг друга, то нам нужно обеспечить реализацию этого метода в нашем классе. Теперь в случаеdefault
final
методов мы не можем предоставить реализацию, и мы застряли. И именно поэтомуfinal
ключевое слово не используется в интерфейсах.источник