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




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




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



вопрос:



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

617   4  

4 ответов:

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

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

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

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

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

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

interface B { 
}

class C implements A, B { 
}

здесь все хорошо; C наследует foo() С A. Теперь предположим, что B заменяется на тег foo метод, по умолчанию:

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

в очередной жаркой дискуссии о методах окончательного защитника на тему Брайан снова сказал:

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

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

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

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

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

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

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

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

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

Comments

    Ничего не найдено.