Reputation: 5859
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?
Upvotes: 80
Views: 10299
Reputation: 1
Functional Interface
@FunctionalInterface
interface Runnable
{
void run();
}
class Race
{
public void run()
{
System.out.println("Running form Race class");
}
}
public class Test
{
public static void main(String[] args) {
//providing implementation for run method through lambda
Runnable r1 = () -> System.out.println("Running from lambda");
r1.run();
//Providing implementation for run method through mehtod reference
Runnable r2 = new Race()::run;
r2.run();
}
}
Upvotes: 0
Reputation: 366
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 {
// ...
}
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.
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:
&&
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:
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:
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) {
// ...
}
sealed
or annotations.Object
are ignored.default
methods are ignored, unless they are later overridden by sub-interfaces as non-default.Upvotes: 0
Reputation: 55
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.
Upvotes: 2
Reputation: 81074
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.
Upvotes: 73
Reputation: 5302
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
}
Upvotes: 1
Reputation: 1104
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 -
More detail with example code https://readtorakesh.com/functional-interface-java8/
Upvotes: -1
Reputation: 173
Definition:
If an interface contains only one abstract method, then such type of interface is called functional interface.
Usage:
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:
Upvotes: -1
Reputation: 56636
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 fromjava.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
Upvotes: 16
Reputation: 68935
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.
Upvotes: 4
Reputation: 61351
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.
Upvotes: -1