Почему "final" не разрешен в методах интерфейса Java 8?

одной из самых полезных функций 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 методы интерфейса еще не были полностью изучены,со ссылкой на Брайан Гетц:

другая часть - это то, как далеко мы собираемся идти, чтобы поддержать классовое строительство инструменты в интерфейсы, такие как final methods, private methods, protected методы, статические методы и др. Ответ: мы еще не знаем!--16-->

С того времени в конце 2011 года, очевидно, поддержка static добавлены методы в интерфейсах. Очевидно, что это добавило много значения самим библиотекам JDK, например, с Comparator.comparing().

вопрос:

в чем причина final (а также static final) никогда не добирался до Java 8 интерфейсы?

4 ответов


этот вопрос в какой-то степени связан с какова причина, по которой "синхронизированный" не разрешен в методах интерфейса Java 8?

ключевая вещь, чтобы понять о методах по умолчанию является то, что основная цель Дизайна эволюция интерфейса, а не"превратить интерфейсы в (посредственные) черты". Хотя между ними есть некоторое перекрытие, и мы пытались приспособиться к последнему, где это не мешало первому, эти вопросы лучше всего понять, когда смотришь в этом свете. (Обратите внимание также, что методы класса are будет отличаться от методов интерфейса, независимо от намерения, в силу того, что методы интерфейса могут быть многократно унаследованы.)

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

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

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

interface A { 
    default void foo() { ... }
}

interface B { 
}

class C implements A, B { 
}

здесь все хорошо; C наследует foo() С A. А теперь предположим ... --5--> изменен на foo метод, по умолчанию:

interface B { 
    default void foo() { ... }
}

теперь, когда мы идем к перекомпиляции C, компилятор скажет нам, что он не знает, какое поведение наследовать для foo(), Так что C должен переопределить его (и может выбрать, чтобы делегировать A.super.foo() если он хотел сохранить то же самое поведение.) Но что, если B сделал свой дефолт final и A не находится под контролем автора C? Теперь C безвозвратно сломан, он не может скомпилировать без переопределения foo(), но он не может переопределять foo() если это было окончательно в B.

это только один пример, но дело в том, что окончательные методы действительно инструмент это имеет больше смысла в мире классов с одним наследованием (как правило, которые связывают состояние с поведением), чем интерфейсы, которые просто способствуют поведению и могут быть многократно унаследованы. Слишком сложно рассуждать о том," какие другие интерфейсы могут быть смешаны в конечном реализаторе", и разрешение метода интерфейса быть окончательным, вероятно, вызовет эти проблемы (и они взорвутся не на человеке, который написал интерфейс, а на бедном пользователе, который пытается его реализовать.)

еще одна причина запретить их, что они не будут иметь в виду то, что вы думаете, что они имеют в виду. Реализация по умолчанию рассматривается только в том случае, если класс (или его суперклассы) не предоставляют объявление (конкретное или абстрактное) метода. Если метод по умолчанию был final, но суперкласс уже реализовал метод, значение по умолчанию будет проигнорировано, что, вероятно, не то, что автор по умолчанию ожидал при объявлении его final. (Это поведение наследования является отражением design center for default methods -- эволюция интерфейса. Должно быть возможно добавить метод по умолчанию (или реализацию по умолчанию к существующему методу интерфейса) к существующим интерфейсам, которые уже имеют реализации, без изменения поведения существующих классов, реализующих интерфейс, гарантируя, что классы, которые уже работали до добавления методов по умолчанию, будут работать таким же образом при наличии методов по умолчанию.)


в списке рассылки lambda есть много дискуссий об этом. Один из тех, которые, кажется, содержат много дискуссий обо всем этом, заключается в следующем: On различная видимость метода интерфейса (были окончательные защитники).

в этой дискуссии, Talden, автор исходный вопрос спрашивает что-то очень похожее на ваш вопрос:

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

Это трудно исправить без добавления какой-либо неясной или совместимости нарушение нюансов языка. Совместимости, что величину и потенциальную тонкость увидишь бессовестной должно существовать решение, которое не нарушает существующий код.

может повторно ввести ключевое слово "package" в качестве спецификатора доступа жизнеспособный. Это отсутствие спецификатора в интерфейсе будет означать public-доступ и отсутствие спецификатора в классе подразумевает пакет-доступ. Какие спецификаторы имеют смысл в интерфейсе, неясно - особенно если, чтобы минимизировать нагрузку на разработчиков, мы должны гарантировать, что спецификаторы доступа означают одно и то же в обоих класс и интерфейс, если они присутствуют.

при отсутствии методов по умолчанию я бы предположил, что спецификатор члена в интерфейс должен быть не менее видимым, чем сам интерфейс (таким образом, интерфейс может быть фактически реализован в все видимые контексты) - с методами по умолчанию, которые не так уверены.

было ли какое-либо четкое сообщение о том, является ли это даже возможное обсуждение в рамках? Если нет, то следует провести его в другом месте.

В конце концов ответ Брайана Гетца было:

Да, это уже изучаются.

однако позвольте мне установить некоторые реалистичные ожидания -- language / VM функции имеют долгое время выполнения, даже тривиальные-кажущиеся такими. Время для предложения новых идей языковых функций для Java SE 8 имеет почти все прошло.

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

в другой жаркой дискуссии о методах final defender на тему :

и вы получили именно то, что вы желали. Именно так. эта функция добавляет -- множественное наследование поведения. Конечно поймите, что люди будут использовать их как черты характера. И мы много работали. чтобы гарантировать, что модель наследования, которую они предлагают, проста и достаточно чистый, чтобы люди могли получить хорошие результаты, делая это в широком различные ситуации. Мы, в то же время, выбрали не толкать их за гранью того, что работает просто и чисто, и что приводит к реакциям "О, вы не зашли достаточно далеко" в некоторых случаях. Но действительно, большая часть этой нити, кажется, ворчит, что стекло просто 98% полный. Я возьму эти 98% и продолжу!

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


будет трудно найти и идентифицировать "ответ", для резонансов, упомянутых в комментариях от @EJP : есть примерно 2 (+/- 2) человека в мире, которые могут дать определенный ответ на всех. И, несомненно, ответ может быть чем-то вроде "поддержка методов окончательного дефолта, по-видимому, не стоит усилий по реструктуризации внутренних механизмов разрешения вызовов". Это, конечно, предположение, но оно, по крайней мере, подкреплено тонкими доказательствами, как это заявление (одним из двух лиц) в списке рассылки OpenJDK:

"Я полагаю, что если были разрешены методы "final default", им может потребоваться переписывание с внутреннего invokespecial на user-visible invokeinterface."

и тривиальные факты, подобные этому методу, просто не считается быть (действительно) окончательный метод это default метод, как в настоящее время реализован в способ::is_final_method метод в OpenJDK.

дальнейшая действительно "авторская" информация действительно трудно найти, даже с чрезмерными websearches и чтением журналов фиксации. Я думал, что это может быть связано с потенциальными неоднозначностями во время разрешения вызовов метода интерфейса с invokeinterface инструкции и и вызовы методов класса, соответствующие invokevirtual инструкции: для invokevirtual инструкция, может быть простой vtable поиск, потому что метод должен быть наследуется от суперкласса или реализуется непосредственно классом. В отличие от этого,invokeinterface вызов должен изучить соответствующий сайт вызова, чтобы узнать , который интерфейс этот вызов фактически относится к (это объясняется более подробно в InterfaceCalls страница Вики HotSpot). Однако,final методы либо не вставляются в vtable вообще, или заменить существующие записи в vtable (см. klassVtable.СРР. Строка 333), и аналогично, методы по умолчанию заменяют существующие записи в vtable (см. klassVtable.cpp, линия 202). Так что фактический причина (и, следовательно, ответ) должна быть скрыта глубже внутри (довольно сложных) механизмов разрешения вызовов метода, но, возможно, эти ссылки все же будут считаться полезными, будь то только для других, которым удается получить фактический ответ из этого.


Я не думаю, что необходимо указать final по методу интерфейса convienience, я могу согласиться, хотя это мая будьте полезны, но, по-видимому, затраты перевешивают преимущества.

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

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