Learning Java 8 Lambdas and just wondering how the compiler knows which method in Comparator to use for the lambda expression?
It doesn't seem to be a SAM interface? It has 2 abstract methods:
#FunctionalInterface
public interface Comparator<T> {
int compare(T o1, T o2);
boolean equals(Object obj);
}
equals() is not an abstract method. This method overrides Object.equals(Object), and is there only for the Comparator interface to be able to have javadoc attached to the method, explaining how comparators should implement equals().
See the javadoc of FunctionalInterface:
If an interface declares an abstract method overriding one of the public methods of java.lang.Object, that also does not count toward the interface's abstract method count since any implementation of the interface will have an implementation from java.lang.Object or elsewhere.
equals() is inherited from Object, and inherited public methods are not counted when you’re determining whether an interface is a functional interface. So even though equals() is abstract in Comparator, because it’s inherited, it doesn’t count.
RULE:
A functional interface is an interface that has one abstract method. Default methods don’t count; static methods don’t count; and methods inherited from Object don’t count.
All classes descend from Object class and Object contains an equal method.
So, this means that every instance that implements Comparator will already have an implementation of equal method.
Therefore Only one method is required to override by the Implanting class of Comparator Interface.
This makes only one abstract method in Comparator interface
This is why Comparator is a functional interface
Related
This question already has answers here:
Precise definition of "functional interface" in Java 8
(9 answers)
Closed 2 years ago.
According to definition of functional interface - A functional interface is an interface that contains only one abstract method.
But Comparator<T> has two abstract methods:
int compare(T o1, T o2);
boolean equals(Object obj);
others are default and static.
JavaDocs mentions it as functional interface. How can it be?
You're reading the wrong definition, or at least, an (over)simplified one.
The proper definition of a FunctionalInterface is:
A functional interface is an interface that has just one abstract method (aside from the methods of Object), and thus represents a single function contract. This "single" method may take the form of multiple abstract methods with override-equivalent signatures inherited from superinterfaces; in this case, the inherited methods logically represent a single method.
SOURCE: Java Language Specification section 9.8
If you look at the source code of Comparator<T> , it is like below:
#FunctionalInterface
public interface Comparator<T> {
// abstract method
int compare(T o1, T o2);
// abstract method, overriding public methods of `java.lang.Object`, so it does not count
boolean equals(Object obj);
}
The equals is an abstract method overriding one of the public methods of java.lang.Object, this doesn’t count as an abstract method.
So In fact The Comparator only has one abstract method i.e int compare(T o1, T o2), and it meet the definition of functional interface.
equals is only explicitly included in the Comparator interface so that they can add some additional JavaDocs to it, e.g. some comparator-specific requirements:
this method can return true only if the specified object is also a comparator and it imposes the same ordering as this comparator
Source: Comparator JavaDocs
The JLS says:
interfaces do not inherit from Object, but rather implicitly declare
many of the same methods as Object (§9.2)
So all the Comparator authors have done is declare something explicitly that is normally implicit.
If the methods of Object counted as abstract methods for the purposes of defining a functional interface then no functional interface would have just a single abstract method. As such, they are not considered:
A functional interface is an interface that has just one abstract
method (aside from the methods of Object)
JLS, emphasis mine
I'm new here and this is my first post. I've just completed my Java OCA and now moving onto studying for the OCP. I have a question regarding Comparable interface.
I have this code snippet which explains how Comparable is implemented:
import java.util.*;
public class Duck implements Comparable<Duck> {
private String name;
public Duck(String name) {
this.name = name;
}
public String toString() { // use readable output
return name;
}
public int compareTo(Duck d) {
return name.compareTo(d.name); // call String's compareTo
}
public static void main(String[] args) {
List<Duck> ducks = new ArrayList<>();
ducks.add(new Duck("Quack"));
ducks.add(new Duck("Puddles"));
Collections.sort(ducks); // sort by name
System.out.println(ducks); // [Puddles, Quack]
}
}
I more or less understand what is goin on here but below this code snippet the author quotes that:
The Duck class implements the Comparable interface. Without implementing that interface, all we have is a method named compareTo(), but it wouldn't be a Comparable object.
My question is why would it not be comparable? Is this something to do with the fact that calling code such as the Collections.sort() would internally use the Comparable type as a reference parameter to compare any object?
Thanks in advance for any help and I hope my question makes sense.
Java is a Object Oriented Based language. Which supports inheritance through classes/ polymorphism through class/abstract class/interface
interface Comparable<T> {
// methods
}
class Person implements Comparable<Person> {
//methods
}
This essentially means any object of the Type Person is also of the Comparable Type.
interface Runnable {}
class Task implements Runnable {}
this means any object created of Task class is also of the Runnable Type.
This is what the author means.
If you do not implement Comparable interface, yet define the compareTo() method, you are just defining a method inside the class, as any other method. YOU ARE NOT OVERRIDING THE compareTo() method in the Comparable interface defined.
You can still compare each object using your compareTo() method, but you need to define your own sort method which internally would call compareTo() method to get the list in a sorted way.
The Java API Collections.sort() internally converts the list to an Object[] and calls the Arrays.sort(). Now Arrays.sort() will use a modified version of the TimSort Algorithm for sorting and the contract is - it does the sorting of elements of the Array only if they are of the Comparable Type.
ComparableTimSort
Collections.sort()
You can check, for all of the internal calls, it states clearly :
#throws IllegalArgumentException (optional) if the comparator is
found to violate the {#link Comparator} contract
So to pass any Object Types to the sort() it has to be also of the type Comparable. Strings/Wrappers are already of the Comparable Type. Hence you need to take care of this contract while defining your user defined objects.
"Without implementing that interface, all we have is a method named compareTo(), but it wouldn't be a Comparable object."
-Simply put, it means without implementing the interface, you have a Duck type object, NOT comparable type
My question is why would it not be comparable? Is this something to do with the fact that calling code such as the Collections.sort() would internally use the Comparable type as a reference parameter to compare any object?
I'm not sure what you exactly mean by reference parameter.
In Java, it is not enough for a class to provide implementation for a method on an interface (or a class) to become that type. It has to be explicitly mentioned in the class declaration.
There are two overloaded sort utility methods on the Collection class.
public static <T extends Comparable<? super T>> void sort(List<T> list)
public static <T> void sort(List<T> list, Comparator<? super T> c)
When your class implements Comparable, you can pass just the list of objects of that class in the first method. If it does not implement Comparable, it will not compile as the bounds for T must extend Comparable. In that case, you will be forced to pass an explicit Comparator to compare the objects.
As the author of the book mentioned, having a method whose signature is the same as the method in an arbitrary interface (or class) does not make it of that type.
Duck Typing does not work in Java
Recently I started exploring Java 8 and I can't quite understand the concept of "functional interface" that is essential to Java's implementation of lambda expressions. There is a pretty comprehensive guide to lambda functions in Java, but I got stuck on the chapter that gives definition to the concept of functional interfaces. The definition reads:
More precisely, a functional interface is defined as any interface that has exactly one abstract method.
An then he proceeds to examples, one of which is Comparator interface:
public interface Comparator<T> {
int compare(T o1, T o2);
boolean equals(Object obj);
}
I was able to test that I can use a lambda function in place of Comparator argument and it works(i.e. Collections.sort(list, (a, b) -> a-b)).
But in the Comparator interface both compare and equals methods are abstract, which means it has two abstract methods. So how can this be working, if the definition requires an interface to have exactly one abstract method? What am I missing here?
From the same page you linked to:
The interface Comparator is functional because although it declares two abstract methods, one of these—equals— has a signature corresponding to a public method in Object. Interfaces always declare abstract methods corresponding to the public methods of Object, but they usually do so implicitly. Whether implicitly or explicitly declared, such methods are excluded from the count.
I can't really say it better.
Another explanation is given in the #FunctionalInterface page:
Conceptually, a functional interface has exactly one abstract method. Since default methods have an implementation, they are not abstract. If an interface declares an abstract method overriding one of the public methods of java.lang.Object, that also does not count toward the interface's abstract method count since any implementation of the interface will have an implementation from java.lang.Object or elsewhere.
You can test which interface is a correct functional interface using #FunctionalInterface.
E.g.:
this works
#FunctionalInterface
public interface FunctionalInterf {
void m();
boolean equals(Object o);
}
this generates an error:
#FunctionalInterface
public interface FunctionalInterf {
void m();
boolean equals();
}
Multiple non-overriding abstract methods found in interface FunctionalInterf
Q. But in the Comparator interface both compare() and equals() methods are abstract, which means it has two abstract methods. So how can this be working, if the definition requires an interface to have exactly one abstract method? What am I missing here?
A.
A functional interface may specify any public method defined by Object, such as equals( ),
without affecting its “functional interface” status. The public Object methods are considered implicit
members of a functional interface because they are automatically implemented by an instance of a
functional interface.
An interface cannot extend Object class, because Interface has to have public and abstract methods.
For every public method in the Object class, there is an implicit public and abstract method in an interface.
This is the standard Java Language Specification which states like this,
“If an interface has no direct super interfaces, then the interface implicitly declares a public abstract member method m with signature s, return type r, and throws clause t corresponding to each public instance method m with signature s, return type r, and throws clause t declared in Object, unless a method with the same signature, same return type, and a compatible throws clause is explicitly declared by the interface.”
That's how Object class' methods are declared in an interface. And according to JLS, this does not count as interface' abstract method. Hence, Comparator interface is a functional interface.
A functional interface has only one abstract method but it can have multiple default and static methods.
Since default methods are not abstract you’re free to add default methods to your functional interface as many as you like.
#FunctionalInterface
public interface MyFuctionalInterface
{
public void perform();
default void perform1(){
//Method body
}
default void perform2(){
//Method body
}
}
If an interface declares an abstract method overriding one of the public methods of java.lang.Object, that also does not count toward the interface’s abstract method count since any implementation of the interface will have an implementation from java.lang.Object or elsewhere.
Comparator is a functional interface even though it declared two abstract methods. Because one of these abstract methods “equals()” which has signature equal to public method in Object class.
e.g. Below interface is a valid functional interface.
#FunctionalInterface
public interface MyFuctionalInterface
{
public void perform();
#Override
public String toString(); //Overridden from Object class
#Override
public boolean equals(Object obj); //Overridden from Object class
}
Here is a "show me the code" approach to understanding the definition:
we shall look into OpenJDK javac for how it checks validity of classes annotated with #FunctionalInterface.
The most recent (as of July, 2022) implementation lies here:
com/sun/tools/javac/code/Types.java#L735-L791:
/**
* Compute the function descriptor associated with a given functional interface
*/
public FunctionDescriptor findDescriptorInternal(TypeSymbol origin,
CompoundScope membersCache) throws FunctionDescriptorLookupError {
// ...
}
Class Restriction
if (!origin.isInterface() || (origin.flags() & ANNOTATION) != 0 || origin.isSealed()) {
//t must be an interface
throw failure("not.a.functional.intf", origin);
}
Pretty simple: the class must be an interface and must not be a sealed one.
Member Restriction
for (Symbol sym : membersCache.getSymbols(new DescriptorFilter(origin))) { /* ... */ }
In this loop, javac goes through the members of the origin class, using a DescriptorFilter to retrieve:
Method members (of course)
&& that are abstract but not default,
&& and do not overwrite methods from Object,
&& with their top level declaration not a default one.
If there is only one method matching all the above conditions, then surely it is a valid functional interface, and any lambda will overwrite that very method.
However, if there are multiple, javac tries to merge them:
If those methods all share the same name, related by override equivalence:
then we filter them into a abstracts collection:
if (!abstracts.stream().filter(msym -> msym.owner.isSubClass(sym.enclClass(), Types.this))
.map(msym -> memberType(origin.type, msym))
.anyMatch(abstractMType -> isSubSignature(abstractMType, mtype))) {
abstracts.append(sym);
}
Methods are filtered out if:
their enclosing class is super class of that of another previously matched method,
and the signature of that previously matched method is subsignature of that of this method.
Otherwise, the functional interface is not valid.
Having collected abstracts, javac then goes to mergeDescriptors, which uses mergeAbstracts, which I will just quote from its comments:
/**
* Merge multiple abstract methods. The preferred method is a method that is a subsignature
* of all the other signatures and whose return type is more specific {#see MostSpecificReturnCheck}.
* The resulting preferred method has a thrown clause that is the intersection of the merged
* methods' clauses.
*/
public Optional<Symbol> mergeAbstracts(List<Symbol> ambiguousInOrder, Type site, boolean sigCheck) {
// ...
}
Conclusion
Functional interfaces must be interfaces :P , and must not be sealed or annotations.
Methods are searched in the whole inheritance tree.
Methods overlapping with those from Object are ignored.
default methods are ignored, unless they are later overridden by sub-interfaces as non-default.
Matching methods must all share the same name, related by override equivalence.
Either there is only one method matching the above conditions, or matching methods can get "merged" by their class hierarchy, subsignature relations, return types and thrown clauses.
The Java docs say:
Note that it is always safe not to override Object.equals(Object).
However, overriding this method may, in some cases, improve
performance by allowing programs to determine that two distinct
comparators impose the same order.
Maybe Comparator is special? Maybe, even though it's an interface, there is somehow a default implementation of equals() that calls compare()? Algorithmically, it's trivial.
I thought all methods that were declared in interfaces were abstract (i. e. no default implementation). But maybe I'm missing something.
Definition:
If an interface contains only one abstract method, then such type of interface is called functional interface.
Usage:
Once we write Lambda expression "->" to invoke its functionality ,
then in this context we require Functional Interface.
We can use the Functional Interface reference to refer Lambda
expression.
Inside functional interface we can have one abstract method and n
number of default/static methods.
Functional interface with respect to inheritance:
If an interface extends Functional interface and the child interface does not contain any abstract method , then the child interface is also considered to be Functional Interface.
Functional interface is not new to java, its already used in following interface API's:
Runnable : contains run() method only.
Callable : contains call() method only.
Comparable : contains compareTo() method only.
Before Java 8, an interface could only declare one or more methods also known as Abstract Method (method with no implementation, just the signature). Starting with Java 8 an interface can also have implementation of one or more methods (knows as Interface Default Method) and static methods along with abstract methods. Interface Default Methods are marked default keyword.
So the question is, what is Functional Interface?
An interface with Single Abstract Method (SAM) is called Functional Interface.
Which means -
An interface with Single Abstract Method is a Functional Interface
An interface with Single Abstract Method and zero or more default
methods and zero or more static method is also a valid Functional
Interface.
More detail with example code https://readtorakesh.com/functional-interface-java8/
I understand that a bunch of methods with body are added using default keyword of Java 8. My question is where is this annotation useful? I see that only one abstract method is allowed to be added with such annotation. But Comparator has two abstract methods :
int compare(T o1, T o2);
boolean equals(Object obj);
Also , Please explain the use of this annotation? I see that its a runtime annotation, so what are its uses ?
From the documentation of FunctionalInterface :
If an interface declares an abstract method overriding one of the public methods of java.lang.Object, that also does not count toward the interface's abstract method count since any implementation of the interface will have an implementation from java.lang.Object or elsewhere.
Since equals is from java.lang.Object, it's not counted. It's the compare method that relates to FunctionInterface.
It is useful since if you add it and you have more than one method in the interface you will get a compile error.
#FunctionalInterfaces are used to let Java know that this piece of code might be replaced with a lambda expression since every lambda is an implementation of some #FunctionalInterface. It helps the compiler know what is the method signature.
Why can't I create a #FunctionalInterface with a default method implementation?
#FunctionalInterface
public interface MyInterface {
default boolean authorize(String value) {
return true;
}
}
You can have default methods in a functional interface but its contract requires you to provide one single abstract method (or SAM). Since a default method have an implementation, it's not abstract.
Conceptually, a functional interface has exactly one abstract method.
Since default methods have an implementation, they are not abstract.
and
If a type is annotated with this annotation type, compilers are
required to generate an error message unless:
The type is an interface type and not an annotation type, enum, or
class.
The annotated type satisfies the requirements of a functional
interface.
Here you don't satisfy the functional interface's requirement, so you need to provide one abstract method. For example:
#FunctionalInterface
interface MyInterface {
boolean authorize(int val);
default boolean authorize(String value) {
return true;
}
}
Note that if you declare an abstract method overriding one of a public method from the Object's class it doesn't count, because any implementation of this interface will have an implementation of those methods through at least the Object's class. For example:
#FunctionalInterface
interface MyInterface {
default boolean authorize(String value) {
return true;
}
boolean equals(Object o);
}
does not compile.
A functional interface is an interface having a single abstract method. The entire purpose of defining functional interfaces is to enable the implementation of the single abstract method via lambda expressions which will effectively override that method which makes providing a default implementation for it pointless.
Having an interface consisting entirely of default methods raises multiple problems. There is the technical problem that the compiler can’t decide for a lambda expression which method to implement when there are multiple default methods and there is the semantic problem that an interface consisting entirely of default methods is not abstract. You can’t instantiate this default behavior as you can’t instantiate interfaces and are forcing programmers to create concrete classes just to invoke the default behavior, which, since interfaces are stateless, could be provided by a singleton instead:
#FunctionalInterface
public interface MyInterface {
static MyInterface DEFAULT = s->true;
boolean authorize(String value);
}
Note that you can have interfaces extending a functional interface and providing a default method, if you need. Still, if this results in creating an interface having no abstract methods I would question the design. You may compare with the discussion about marker interfaces with default methods. If the sub-interface will have different abstract methods than the functional interface, it’s a different story. There might be real use cases for this, but these sub-interfaces will also demonstrate why they shouldn’t be mixed with the functional base interface as a lambda expression will always implement the abstract method.
That's because #FunctionalInterface can have default methods, as many as you want. For example, consider the java.util.Function interface. It contains two default methods: compose and andThen. But there should be exactly one non-default method. Otherwise how compiler would know which of your default methods should be mapped to lambda?
I just want to add a few more points.
We can have any number of Abstract method in FuntionalInterface.
We can also have any number of Static method in FuntionalInterface.
We can also declare an abstract method overriding one of a public method from the Object's class but there must be some other custom abstract method in this functional interface too as shown in below code
#FunctionalInterface
public interface SAM {
public void helloSam();
default void xyz() {
System.out.println("xyz");
}
static void abc() {
System.out.println("abc");
}
static void abc1() {
System.out.println("abc1");
}
default void xyz1() {
System.out.println("xyz1");
}
boolean equals(Object o);
}