Почему @FunctionalInterface не используется на всех интерфейсах в JDK, которые соответствуют критериям?

Java 8 предоставила нам много интересных способов использования функциональных интерфейсов, а вместе с ними и новую аннотацию: @FunctionalInterface. Его задача - сказать компилятору кричать на нас, если мы не будем придерживаться правил функционального интерфейса (только один абстрактный метод, который нуждается в переопределении, пожалуйста).

В пакете java.util.function есть 43 интерфейса с этой аннотацией. Поиск jdk.1.8.0/src для @FunctionalInterface только включает 57 ударов. Почему другие интерфейсы (такие как AutoCloseable), которые могли бы добавить @FunctionalInterface, все еще отсутствовали?

В документации аннотаций есть немного неопределенный намек:

"Информативный тип аннотации, используемый для указания того, что объявление типа интерфейса предназначено как функциональный интерфейс

Есть ли веская причина НЕ для намереваться, что интерфейс, который я разработал (который может просто оказаться функциональным интерфейсом), не может использоваться как один? Оставляет ли это указание на что-либо, кроме того, что он не понимает, что его можно было бы добавить?

Не добавляет ли абстрактные методы к любому опубликованному интерфейсу, который будет винить любого, кто его реализует, функциональный или нет? Я чувствую себя циничным, полагая, что они просто не потрудились охотиться на всех, но какое еще объяснение есть?

Обновить: после просмотра "Должен ли" Comparable "быть" функциональным интерфейсом "?" Я считаю, что у меня все еще есть проблемы с ворчанием. Когда интерфейс с одним интерфейсом и функциональный интерфейс структурно идентичны, что осталось быть другим? Является ли разница просто именами? Сопоставимые и компараторы достаточно близки к тому же семантически. Оказывается, они разные структурно, хотя так еще не лучший пример...

Есть ли случай, когда SMI структурно хорош для использования в качестве функционального интерфейса, но все же не рекомендуется использовать семантический смысл имени интерфейса и метода? Или, возможно, контракт, подразумеваемый Javadocs?

Ответ 1

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

Вы назвали пример AutoCloseable, который, очевидно, не предназначен для реализации как функция asres Runnable, что намного удобнее для функции с ()->void сигнатурой. Предполагается, что класс, реализующий AutoCloseable, управляет внешним ресурсом, который анонимные классы, реализованные с помощью выражения лямбда, не делают.

Более понятный пример: Comparable, interface не только не предназначен для реализации в качестве лямбда-выражения, но и для его правильной реализации с использованием выражения лямбда.


Возможные причины не маркировать interface с помощью @FunctionalInterface на примере:

  • interface имеет семантику языка программирования, например. AutoClosable или Iterable (что вряд ли произойдет для ваших собственных интерфейсов)
  • Не ожидается, что interface имеет произвольные реализации и/или больше идентификатор, чем фактическая реализация, например. java.net.ProtocolFamily или java.lang.reflect.GenericArrayType (обратите внимание, что последний также наследует реализацию default для getTypeName(), бесполезную для лямбда-реализаций, полагаясь на toString())
  • Экземпляры этого interface должны иметь идентификатор, например. java.net.ProtocolFamily, java.nio.file.WatchEvent.Modifier и т.д. Обратите внимание, что они обычно реализуются с помощью enum

    Другим примером является java.time.chrono.Era, который имеет только один метод abstract, но его спецификация говорит:" Экземпляры Era могут сравниваться с помощью оператора ==.

  • interface предназначен для изменения поведения операции, для которой реализация interface без наследования/реализации чего-либо еще не имеет смысла, например. java.rmi.server.Unreferenced
  • Это абстракция общих операций классов, которые должны иметь не только эти операции, например. java.io.Closeable, java.io.Flushable, java.lang.Readable
  • Ожидаемое наследование является частью контракта и запрещает реализацию лямбда-выражения, например. в java.awt: ActiveEvent должен быть реализован с помощью AWTEvent, PrinterGraphics на Graphics, то же самое относится к java.awt.print.PrinterGraphics (hey, two interface для точно такой же вещи...), где javax.print.FlavorException должен быть реализован подклассом javax.print.PrintException
  • Я не знаю, поддерживает ли различные интерфейсы прослушивателя событий, отмеченные @FunctionalInterface для симметрии с другим прослушивателем событий нескольких методов, которые не могут быть функциональными интерфейсами, но на самом деле прослушиватели событий являются хорошими кандидатами для лямбда-выражений. Если вы хотите удалить слушателя позже, вам нужно сохранить экземпляр, но он не отличается от, например. внутренние реализации слушателя.
  • У хранителя библиотеки есть большая база кода с более чем 200 типами кандидатов, а не ресурсы для обсуждения для каждого interface, следует ли ее аннотировать и, следовательно, основное внимание уделяется основным кандидатам для использования в функциональном контексте. Я уверен, что, например, java.io.ObjectInputValidation, java.lang.reflect.InvocationHandler, juc RejectedExecutionHandler и ThreadFactory не будут плохими, как @FunctionalInterface, но я не знаю, будет ли, например. java.security.spec.ECField делает хорошего кандидата. Чем более общей является библиотека, тем более вероятные пользователи библиотеки смогут ответить на этот вопрос для конкретного interface, который им интересен, но было бы несправедливо настаивать на том, чтобы разработчик библиотеки отвечал на него для всех интерфейсов.

    В этом контексте имеет смысл видеть наличие @FunctionalInterface в качестве сообщения о том, что interface определенно предназначено для использования вместе с лямбда-выражениями, чем рассматривать отсутствие аннотации в качестве индикатора ее не предназначенный для использования таким образом. Это точно так же, как компилятор справляется с этим, вы можете реализовать каждый абстрактный метод interface с помощью лямбда-выражения, но когда присутствует аннотация, он будет гарантировать, что вы можете использовать этот interface таким образом.

Ответ 2

Планируемое расширение. Просто потому, что интерфейс соответствует требованиям SMI, это не означает, что расширение не требуется позже.

Ответ 3

В java 8 функциональный интерфейс представляет собой интерфейс, имеющий только один абстрактный метод, называемый функциональным методом, к которому сопоставляются параметр лямбда-выражений и типы возвращаемых данных.

java.util.function содержит функциональные интерфейсы общего назначения, используемые JDK, а также доступные для конечных пользователей. Хотя они не являются полным набором функциональных интерфейсов, к которым могут быть применены лямбда-выражения, но они обеспечивают достаточно для удовлетворения общих требований. Вы можете создавать свои собственные функциональные интерфейсы всякий раз, когда существующего набора недостаточно.

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

Например, просмотрите следующий код.

public interface Comparable<T> {
   public int compareTo(T o);
}

@FunctionalInterface
public interface ToIntFunction<T> {
   int applyAsInt(T value);
}

public static void main(String[] args){
   ToIntFunction<String> f = str -> Integer.parseInt(str);
   Comparable<String> c = str -> Integer.parseInt(str);
}

Comparable также может взять объект и получить некоторое значение типа int, но для выполнения этой задачи предусмотрен более общий выделенный интерфейс ToIntFunction. Не существует такого жесткого правила, что все заслуживающие интерфейсы должны быть аннотированы с помощью @FunctionalInterface, но чтобы получить преимущество лямбда-функции, интерфейс должен выполнять все критерии, определенные FunctionalInterface.