Почему @FunctionalInterface не используется на всех интерфейсах в JDK, которые квалифицируются?
Java 8 предоставил нам много интересных способов использования функциональных интерфейсов, а вместе с ними и новую аннотацию: @FunctionalInterface . Его задача – сказать компилятору кричать на нас, если мы не будем придерживаться правил функционального интерфейса (только один абстрактный метод, который требует переопределения).
В этом пакете java.util.function имеется 43 интерфейса с этой аннотацией. Поиск jdk.1.8.0 / src для @FunctionalInterface вызывает только 57 ударов. Почему другие интерфейсы (такие как AutoCloseable), которые могли бы добавить @FunctionalInterface
все еще отсутствуют?
В документации аннотаций есть немного неопределенный намек:
- Точное определение «функционального интерфейса» в Java 8
- Можете ли вы вызвать метод по умолчанию родительского интерфейса из интерфейса, который подclassифицирует этот интерфейс?
- Использовать ссылку на метод с параметром
«Информативный тип annotations, используемый для указания того, что объявление типа интерфейса предназначено для функционального интерфейса»
Есть ли веская причина НЕ намереваться, что интерфейс, который я разработал (который может просто оказаться функциональным интерфейсом), не может использоваться как один? Оставляет ли это указание на что-либо, кроме того, что он не понимает, что его можно было бы добавить?
Не добавляет ли абстрактные методы к любому опубликованному интерфейсу, который будет винить любого, кто его реализует, функциональный или нет? Я чувствую себя циничным, полагая, что они просто не потрудились охотиться на всех, но какое другое объяснение есть?
Обновление : после просмотра «Должно ли« Comparable »быть« функциональным интерфейсом »? Я нахожу, что у меня все еще есть вопросы. Когда единый интерфейс метода и функциональный интерфейс структурно идентичны, что осталось быть другим? Является ли разница просто именами? Сопоставимые и компараторы достаточно близки к тому же семантически. Оказывается, они разные структурно, хотя так еще не лучший пример …
Есть ли случай, когда SMI структурно хорош для использования в качестве функционального интерфейса, но все же не рекомендуется использовать семантический смысл имени интерфейса и метода? Или, может быть, контракт, подразумеваемый Javadocs?
Ну, аннотация, документирующая намерение, бесполезна, если вы предполагаете, что всегда есть это намерение.
Вы назвали пример AutoCloseable
который, очевидно, не предназначен для реализации как функция, так как существует Runnable
что намного удобнее для функции с ()->void
сигнатурой. Предполагается, что class, реализующий AutoCloseable
управляет внешним ресурсом, который анонимные classы, реализованные с помощью lambda-выражения, не выполняют.
Более ясным примером является Comparable
, interface
не только не предназначен для реализации в виде lambda-выражения, его невозможно реализовать с использованием выражения lambda.
Возможные причины не маркировать interface
с помощью @FunctionalInterface
на примере:
-
interface
имеет семантику языка программирования, напримерAutoClosable
илиIterable
(это вряд ли произойдет для ваших собственных интерфейсов) - Не предполагается, что
interface
имеет произвольные реализации и / или больше идентификатор, чем фактическая реализация, напримерjava.net.ProtocolFamily
, илиjava.lang.reflect.GenericArrayType
(обратите внимание, что последний также наследует реализацию поdefault
дляgetTypeName()
бесполезно для lambda-реализаций, полагаясь наtoString()
) -
Экземпляры этого
interface
должны иметь идентификатор, напримерjava.net.ProtocolFamily
,java.nio.file.WatchEvent.Modifier
и т. Д. Обратите внимание, что они обычно реализуютсяenum
Другим примером является
java.time.chrono.Era
который имеет только одинabstract
метод, но его спецификация говорит: «ЭкземплярыEra
можно сравнить с помощью оператора==
». -
interface
предназначен для изменения поведения операции, для которой реализацияinterface
без наследования / реализации чего-либо еще не имеет смысла, например,java.rmi.server.Unreferenced
- Это абстракция общих операций classов, которые должны иметь не только эти операции, например
java.io.Closeable
,java.io.Flushable
,java.lang.Readable
- Ожидаемое наследование является частью контракта и запрещает реализацию lambda-выражения, например, в
java.awt
:ActiveEvent
должен быть реализованAWTEvent
,PrinterGraphics
Graphics
, то же самое относится кjava.awt.print.PrinterGraphics
(эй, дваinterface
s для точно такой же вещи …), еслиjavax.print.FlavorException
должен быть реализован подclassомjavax.print.PrintException
- Я не знаю, не связаны ли различные интерфейсы прослушивателя событий с
@FunctionalInterface
для симметрии с другим прослушивателем событий нескольких методов, которые не могут быть функциональными интерфейсами, но на самом деле прослушиватели событий являются хорошими кандидатами для lambda-выражений. Если вы хотите удалить слушателя позднее, вы должны сохранить экземпляр, но это не отличается от, например, реализации внутреннего classа слушателя. -
У хранителя библиотеки есть большая база кода с более чем 200 типами кандидатов, а не ресурсы для обсуждения для каждого
interface
, следует ли ее аннотировать и, следовательно, основное внимание уделяется основным кандидатам для использования в функциональном контексте. Я уверен, что, например,java.io.ObjectInputValidation
,java.lang.reflect.InvocationHandler
, jucRejectedExecutionHandler
&ThreadFactory
не будет плохой как@FunctionalInterface
но я понятия не имею, например,java.security.spec.ECField
делает хороший кандидат. Чем более общей является библиотека, тем более вероятным пользователям библиотеки будет возможность ответить на этот вопрос для конкретногоinterface
им интересен, но было бы несправедливо настаивать на том, чтобы разработчик библиотеки отвечал на него для всех интерфейсов.В этом контексте имеет смысл видеть присутствие
@FunctionalInterface
в качестве сообщения о том, чтоinterface
определенно должен использоваться вместе с lambda-выражениями, чем рассматривать отсутствие annotations в качестве индикатора, поскольку он не предназначен для использования сюда. Это точно так же, как компилятор справляется с этим, вы можете реализовать каждый отдельныйinterface
абстрактного метода, используя выражение lambda, но когда присутствует аннотация, он будет гарантировать, что вы можете использовать этотinterface
таким образом.
Планируемое расширение. Просто потому, что интерфейс соответствует требованиям SMI, это не означает, что расширение не понадобится позже.
В java 8 функциональный интерфейс является интерфейсом, имеющим ровно один абстрактный метод, называемый функциональным методом, к которому сопоставляются параметр выражения lambda и возвращаемые типы.
java.util.function
содержит функциональные интерфейсы общего назначения, используемые JDK, а также доступные для конечных пользователей . Хотя они не являются полным набором функциональных интерфейсов, к которым могут быть применены lambda-выражения, но они обеспечивают достаточно для удовлетворения общих требований. Вы можете создавать свои собственные функциональные интерфейсы всякий раз, когда существующего набора недостаточно.
Существует много таких интерфейсов, которые заслуживают того, чтобы их можно было назвать функциональным интерфейсом, но пакет java.util.function
уже предоставляет функциональные интерфейсы для наших почти всех целей.
Например, посмотрите на следующий код.
public interface Comparable { public int compareTo(T o); } @FunctionalInterface public interface ToIntFunction { int applyAsInt(T value); } public static void main(String[] args){ ToIntFunction f = str -> Integer.parseInt(str); Comparable c = str -> Integer.parseInt(str); }
Comparable
может также взять объект и получить некоторое значение типа int, но для выполнения этой задачи предусмотрен более общий выделенный интерфейс ToIntFunction
. Не существует такого жесткого правила, что все заслуживающие интерфейсы должны быть аннотированы с помощью @FunctionalInterface
но для того, чтобы получить преимущество lambda-функции, интерфейс должен выполнять все критерии, определенные FunctionalInterface.