Является ли сопоставимый интерфейс функциональным интерфейсом? - PullRequest
2 голосов
/ 30 июня 2019

Я использую инструменты декомпилятора, когда я смотрю на Comparable, он имеет один абстрактный метод и не имеет FunctionalInterface аннотации

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

такой же, как Comparator, но компаратор имеет FunctionalInterface аннотации

    @FunctionalInterface
    public interface Comparator<T> {
    int compare(T var1, T var2);
    ... 
}

Ответы [ 5 ]

4 голосов
/ 30 июня 2019

Это FunctionalInterface? Нет. Comparable не имеет этой аннотации.

Это функциональный интерфейс ? Да. Из определения в спецификации языка :

Функциональный интерфейс - это интерфейс, который имеет только один абстрактный метод (кроме методов Object)

Будет ли это рассматриваться как FunctionalInterface? Да

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

Является ли логически функциональным интерфейсом? Нет: Comparable не представляет функцию. Это больше похоже на черту объекта. «Эту вещь можно сравнить», а не «эта вещь сравнивает».

Есть ли практическое применение в качестве функционального интерфейса? Я бы сказал, что нет никакого очевидного использования, о чем свидетельствует тот факт, что вы редко (если вообще когда-либо) увидите, что Comparable<Something> используется в качестве типа переменной, параметра или поля; только в предложениях extends или implements и в границах переменных типа.

2 голосов
/ 30 июня 2019

Я нашел ответ в Глава 3 Книги Java [144 страница]

enter image description here

Is Comparable a Functional Interface?

Мы сказали, что Comparator - это функциональный интерфейс, потому что он имеет единый абстрактный метод. Comparable также функциональный интерфейс поскольку он также имеет один абстрактный метод. Однако, используя лямбду Сравнимо было бы глупо. Смысл Comparable заключается в реализации Поместите его внутрь сравниваемого объекта.

1 голос
/ 30 июня 2019

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

9.6.4.9. @ FunctionalInterface

Поскольку некоторые интерфейсы работают случайно, это не так. необходимо или желательно, чтобы все объявления функциональных интерфейсов быть аннотированным @ FunctionalInterface.

Конечно, вы можете использовать его в лямбда-выражении, полагаясь на внешний экземпляр Foo, а не this, как мы обычно делаем в реализации compareTo():

Foo one = new Foo(...);
Comparable<Foo> comparableForOne = other -> one.getX().compareTo(other.getX()));

Foo anotherFoo = new Foo(...);
int result = comparableForOne.compareTo(anotherFoo);

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

1 голос
/ 30 июня 2019

Сравнимым является объект, который можно сравнить с чем-то другим; compareTo() предполагается для сравнения значения this.

Не имеет смысла реализовывать это как лямбду.

0 голосов
/ 30 июня 2019

Чтобы полностью ответить на этот вопрос, мы должны заглянуть под капот Java, чтобы увидеть, что представляют собой лямбды (по крайней мере, на данный момент): синтаксический сахар для реализации интерфейсов с помощью только одного метода. Давайте объясним это на примере Comparator<T> (который прекрасно подходит для лямбда-выражения) и перейдем к Comparable<T> (который может быть реализован лямбда-выражением, но не имеет смысла).

Предположим, кто-то дал нам (скомпилировано)

public class Car {
    private final int horsePowers;
    private final int topSpeed;
    private final int numDoors;
    private final String color;
    // Setters omitted for brevity
}

Мы не можем изменить этот класс, но мы хотим отсортировать автомобили, например, по цвету. Традиционным способом было бы позвонить Collections.sort(List<T> list, Comparator<? super T> c). Поскольку мы, как разработчики, часто ленивы (в противном случае мы не будем использовать Copy-Paste-Programming ), мы часто внедряем подобные вещи в анонимный класс :

...
List<Car> cars = ...;
Collections.sort(cars, new Comparator<Car> {
    @Override
    public int compare(final Car lhs, final Car rhs) {
        return lhs.getColor.compareTo(rhs.getColor());
    }
});

Это безобразно. Во-первых, это долго, а во-вторых, это неудобно читать. Но, к счастью, боги Java благословили нас лямбда-выражениями , так что мы напишем 1 :

...
List<Car> cars = ...;
Collections.sort(cars, (lhs, rhs) -> lhs.getColor().compareTo(rhs.getColor()));

Это выглядит лучше, не так ли? Это коротко, всесторонне, и приятно читать (по крайней мере, если вы видели это сто раз). Но под капотом это все еще старый анонимный класс. Ничего больше. Некоторые ограничения действительно применяются, хотя к лямбдам. Во-первых, у лямбды нет состояния (переменные экземпляра). Во-вторых, все ссылки, используемые за пределами лямбды (локальные переменные или атрибуты окружающего метода / класса), должны быть final или эффективно final (лямбда не может изменять значение). Это станет важным ниже.

Теперь давайте перейдем к Comparable<T>. В то время как Comparator<T> определяет, как сравнивать два T друг с другом, Comparable<T> говорит, что «этот класс сопоставим с T (как правило, сам по себе), и вот как его сравнивать с T». Давайте представим, что наш класс Car сверху фактически реализует Comparable<Car> следующим образом:

public class Car implements Comparable<Car> {
    ...
    public int CompareTo(final Car that) {
        int diff = this.getHorsePowers() - that.getHorsePowers();
        if (diff == 0) {
            diff = this.getTopSpeed() - that.getTopSpeed();
        }
        if (diff == 0) {
            diff = this.getNumDoors() - that.getNumDoors();
        }
        if (diff == 0) {
            diff = this.getColor().compareTo(that.getColor()); 
        }
        return diff;
    }
}

По сути, он сначала сравнивает Car с их horsePowers. Если они равны, он сравнивает их по topSpeed. Если они снова равны, он сравнивает их по количеству дверей и т. Д.

Теперь давайте подведем итоги: лямбда-выражения являются синтаксическим сахаром для реализации интерфейсов с помощью только одного метода, а Comparable<T> описывает, как класс хочет сравнивать с T. Теперь вспомните, что я сказал выше о лямбдах и состояниях: лямбды не имеют никакого состояния. Таким образом, сравнивать их с чем-либо в большинстве ситуаций бессмысленно (нет состояния - сравнивать нечего; я говорю «большую часть времени», поскольку могут быть угловые случаи, когда такая вещь возможна). Так что на самом деле очень мало причин для реализации Comparable<T> с помощью лямбды.

В заключение можно сказать, что хотя можно реализовать Comparable<T> с помощью лямбды, это редко полезно.


1 Я знаю, что лямбда сравнения обычно записывается как Comparator.comparing(Car::getColor), но это помимо того, что я пытаюсь сделать.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...