Is Comparable Interface a FunctionalInterface?

Viewed 4813

I am using decompiler tools when I look at Comparable it has one abstract method and not have FunctionalInterface annotation

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

same like Comparator, but comparator have FunctionalInterface annotation

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

Is it a FunctionalInterface? No. Comparable doesn't have that annotation.

Is it a functional interface? Yes. From the definition in the language spec:

A functional interface is an interface that has just one abstract method (aside from the methods of Object)

Will it be treated as a FunctionalInterface? Yes:

However, the compiler will treat any interface meeting the definition of a functional interface as a functional interface regardless of whether or not a FunctionalInterface annotation is present on the interface declaration.

Is it logically a functional interface? No: Comparable doesn't represent a function. It is more like a trait of an object. "This thing can be compared", rather than "this thing does the comparing".

Is there any practical use of it as a functional interface? I would claim there is no obvious use, as evidenced by the fact you will rarely (if ever) see Comparable<Something> being used as the type of a variable, parameter or field; only in extends or implements clauses, and in the bounds of type variables.

Literally Comparable is a functional interface as it declares one and only one abstract method.
But annotating it with @FunctionalInterface would mean that it is documented to be used as a functional interface while that is not the case at all.
The JLS points out that :

9.6.4.9. @FunctionalInterface

Because some interfaces are functional incidentally, it is not necessary or desirable that all declarations of functional interfaces be annotated with @FunctionalInterface.

Of course you could use it in a lambda by relying on a external Foo instance and not this as we generally do in compareTo() implementation :

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

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

But that would be undesirable since a misuse of the Comparable interface that is designed to work for any instance of the class and not a specific instance.

I found the answer in Java Book Chapter 3 [144 page]

enter image description here

Is Comparable a Functional Interface?

We said that Comparator is a functional interface because it has a single abstract method. Comparable is also a functional interface since it also has a single abstract method. How- ever, using a lambda for Comparable would be silly. The point of Comparable is to imple- ment it inside the object being compared.

A comparable is an object that can be compared to something else; compareTo() is supposed to compare the value of this.

It wouldn't make sense to implement that as a lambda.

To fully answer the question, we should look under the hood of Java to see what lambdas (at lest as of now) are: syntactic sugar for implementing interfaces with exactly one method. Let's explain it with the example of Comparator<T> (which is perfectly fine to be implemented by a lambda) and go over to Comparable<T> (which can be implemented by a lambda, but does not make sense).

Suppose someone has given us a (compiled)

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

We cannot modify this class, but we want to sort cars by, e.g., color. The traditional way would be to call Collections.sort(List<T> list, Comparator<? super T> c). Since we, as developers, are often lazy (otherwise, we would not use Copy-Paste-Programming), we often implement this kind of stuff in an anonymous class:

...
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());
    }
});

This is ugly. First, it is long and second, it is awkward to read. But luckily the Java gods have blessed us with lambda expressions, so that we shall write1:

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

This looks better, doesn't it? It's short, comprehensive, and pleasant to read (at least, if you have seen it a hundred times). But under the hood, it is still an old, anonymous class. Nothing else. Some limitations do apply though to lambdas. First, the lambda does not have any state (instance variables). Second, all references used from outside the lambda (local variables or attributes of the surrounding method/class) must be final or effectively final (the lambda is not allowed to change the value). This will get important further below.

Now, let's come to Comparable<T>. While a Comparator<T> defines how to compare two T's with each other, Comparable<T> says "this class is comparable with T (typically itself), and here is how to compare it with T". Let us imagine that our Car class from above actually implements Comparable<Car> like so:

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;
    }
}

In essence it compares Cars first by their horsePower. If they are equal, it compares them by their topSpeed. If they are equal again, it compares them by the number of doors and so on.

Now let's recap: Lambdas are syntactic sugar for implementing interfaces with exactly one method and Comparable<T> describes how a class wants to be compared to T. Now recall what I have said above about Lambdas and states: Lambdas do not have any state. Thus, comparing them with anything is in most situations pointless (no state - nothing to compare; I say "most of the time" since there might be corner cases where such a thing might be feasible). So there is very little reason to actually implement Comparable<T> with a lambda.

In conclusion, we can say that while it is possible to implement Comparable<T> with a lambda, it is seldom useful.


1I know that the comparison lambda would normally writte as Comparator.comparing(Car::getColor), but this is besides the point I am trying to make.

Related