Method ReferencesJ8 Home « Method References
In this lesson we take an overview of functional interfaces which were introduced in Java8 and are interfaces containing a single abstract method, this does not include declaration of an abstract method overriding one of the public methods of java.lang.Object
since any implementation of the interface will have an implementation from java.lang.Object
or elsewhere.
A functional interface can have any number of default and static methods, as long as there is only a single abstract method it is a functional interface. Functional interfaces can be annotated with the @FunctionalInterface
annotation, to ensure that the functional interface can’t have more than one abstract method, although this isn't mandatory. Interfaces annotated with the @FunctionalInterface
annotation will be flagged in the compiler if more than one abstract method is present excluding those from java.lang.Object
as mentioned above.
The following interfaces that are familiar to Java programmers ActionListener
, Comparable
and Runnable
are now functional interfaces from Java8 onwards. Before Java 8, we had to create anonymous inner class objects or implement these interfaces to use them but from Java8 they can be used via assignment of a lambda expression or method reference.
The following code example illustrates usage, we will go into much greater detail in the appropriate lessons.
package info.java8;
/*
Test class for Bus
*/
public class TestBus {
public static void main (String[] args) {
Bus a1 = new Bus();
a1.setChassis("4-axle chassis");
a1.setMotor("12 stroke");
a1.setWheels(8);
a1.setConductor(true);
System.out.println("Our bus has a " + a1.getChassis() + ", " + a1.getMotor()
+ " motor and has " + a1.getWheels() + " wheels.");
if (a1.getConductor()) {
System.out.println("This bus has a driver and a conductor.");
} else {
System.out.println("This bus has a driver only.");
}
a1.service(3);
a1.carry(60);
a1.load("people.");
a1.queue(12);
a1.payFare(2);
}
}
Predefined Functional Interfaces Top
Java8 comes with many predefined functional interfaces for us to use 'straight out of the box' and which are listed in the table below.
Functional Interface | Description | Abstract Method | Other Methods |
---|---|---|---|
Within java.util.function package | |||
BiConsumer<T,U> | Represents an operation that accepts two input arguments and returns no result. | accept | andThen (default) |
BiFunction<T,U,R> | Represents a function that accepts two arguments and produces a result. | apply | andThen (default) |
BinaryOperator<T> | Represents an operation upon two operands of the same type, producing a result of the same type as the operands. | apply | andThen (default)maxBy (static)minBy (static) |
BiPredicate<T,U> | Represents a predicate (boolean-valued function) of two arguments. | test | and (default)negate (default)or (default) |
Consumer<T> | Represents an operation that accepts a single input argument and returns no result. | accept | andThen (default) |
Function<T,R> | Represents a function that accepts one argument and produces a result. | apply | andThen (default)compose (default)identity (static) |
Predicate<T> | Represents a predicate (boolean-valued function) of one argument. | test | and (default)isEqual (static)negate (default)or (default) |
Supplier<T> | Represents a supplier of results. | get | |
UnaryOperator<T> | Represents an operation on a single operand that produces a result of the same type as its operand. | apply | andThen (default)compose (default)identity (static) |
Existing interfaces that are functional in Java8 | |||
ActionListener | Listener for receiving action events. | actionPerformed | |
Runnable | Runs an action without arguments or return value. | run | |
Comparator | Comparison function, which imposes a total ordering on some collection of objects. | compare | reversed (default)thenComparing (default)thenComparingDouble (default)thenComparingInt (default)thenComparingLong (default)comparing (static)thenComparingDouble (static)thenComparingInt (static)thenComparingLong (static)equal naturalOrder (static)nullsFirst (static)nullsLast (static)reverseOrder (static) |
Primitive Type Specializations Top
Functional Interface | Description | Abstract Method | Other Methods |
---|---|---|---|
BiConsumer<T,U> | Represents an operation that accepts two input arguments and returns no result. | accept | andThen (default) |
BiFunction<T,U,R> | Represents a function that accepts two arguments and produces a result. | apply | andThen (default) |
BinaryOperator<T> | Represents an operation upon two operands of the same type, producing a result of the same type as the operands. | apply | andThen (default)maxBy (static)minBy (static) |
BiPredicate<T,U> | Represents a predicate (boolean-valued function) of two arguments. | test | and (default)negate (default)or (default) |
BooleanSupplier | Represents a supplier of boolean-valued results. | getAsBoolean | |
Consumer<T> | Represents an operation that accepts a single input argument and returns no result. | accept | andThen (default) |
Function<T,R> | Represents a function that accepts one argument and produces a result. | apply | andThen (default)compose (default)identity (static) |
UnaryOperator<T> | Represents an operation on a single operand that produces a result of the same type as its operand. | apply | andThen (default)compose (default)identity (static) |
package info.java8;
/*
Test class for Bus
*/
public class TestBus {
public static void main (String[] args) {
Bus a1 = new Bus();
a1.setChassis("4-axle chassis");
a1.setMotor("12 stroke");
a1.setWheels(8);
a1.setConductor(true);
System.out.println("Our bus has a " + a1.getChassis() + ", " + a1.getMotor()
+ " motor and has " + a1.getWheels() + " wheels.");
if (a1.getConductor()) {
System.out.println("This bus has a driver and a conductor.");
} else {
System.out.println("This bus has a driver only.");
}
a1.service(3);
a1.carry(60);
a1.load("people.");
a1.queue(12);
a1.payFare(2);
}
}
Save, compile and run the file in directory c:\_OOConcepts in the usual way.

The above screenshot shows the output of running our TestBus
test class, everything works fine. We will code up our Taxi
class as well for a bit of practice and for use in the next part of the lesson:
package info.java8;
public class Taxi extends Vehicle implements PublicTransport {
/*
Our queue() implementation
*/
public void queue(int people) {
System.out.println("There are " + people + " people waiting at the taxi rank.");
}
/*
Our payFare() implementation
*/
public void payFare(int fare) {
System.out.println("This taxi ride will cost you " + fare + " pounds.");
}
}
Save and compile the file in directory c:\_OOConcepts in the usual way.
Implementing Multiple Interfaces Top
It is also perfectly legal to implement multiple interfaces and the following code shows the syntax for doing this:
package info.java8;
public class Taxi extends Vehicle implements PublicTransport, Serializable, Runnable {
...
}
Deeper Into Polymorphism Top
So we have found a way to get around multiple inheritance by using interfaces but what are we really getting from this? The interfaces don't contain any implementation code as they are purely abstract and we have to implement them in the first concrete subclass. The answer is polymorphism! We can pass a polymorphic interface type as an argument and return a polymorphic interface type as well. A slideshow is in order to set up the scenario:

We are going to make the Plane
superclass and PassengerPlane
subclass nice and simple and we won't bother with the MilitaryPlane
code as it doesn't add to the objective:
package info.java8;
/*
A Plane class
*/
public class Plane {
}
/*
A PassengerPlane class
*/
public class PassengerPlane extends Plane implements PublicTransport {
/*
Our queue() implementation
*/
public void queue(int people) {
System.out.println("There are " + people + " people waiting to board this plane.");
}
/*
Our payFare() implementation
*/
public void payFare(int fare) {
System.out.println("This plane journey will cost you " + fare + " pounds.");
}
}
/*
Test class for PassengerPlane
*/
public class TestPassengerPlane {
public static void main (String[] args) {
PassengerPlane plane = new PassengerPlane();
plane.queue(152);
plane.payFare(502);
}
}
Save and compile the Plane
, PassengerPlane
and TestPassengerPlane
classes and then run the TestPassengerPlane
class in directory c:\_OOConcepts in the usual way.

The above screenshot shows the output of running our TestPassengerPlane
class, as you can see it's very easy to implement interfaces in multiple hierarchies.
Polymorphic Interface Arguments Top
Ok, we have seen how easy it is to implement interfaces in multiple hierarchies but what about the polymorphism? Well because the implementations can come from different hierarchies we can use the interface type as the reference type when creating our objects. As we know, an implemented abstract type is exactly the same as an overridden method type and so will use polymorphic method invocation to dynamically invoke the method of the the actual object type at runtime. This means we can group together objects from different hierarchies under the umbrella of the interface type as the reference type knowing the correct object will be always be invoked at runtime. Now we are ready to see polymorphic interface arguments in action and for this we need a couple of new classes:
package info.java8;
/*
A Carrier class
*/
public class Carrier {
private int people;
private int cost;
Carrier(int people, int cost) {
this.people = people;
this.cost = cost;
}
public void travelInfo (PublicTransport pt) {
pt.queue(people);
pt.payFare(cost);
}
}
/*
Test class for polymorphic interface arguments
*/
public class TestPolyIntArgs {
public static void main (String[] args) {
PublicTransport bus = new Bus();
PublicTransport taxi = new Taxi();
PublicTransport pp = new PassengerPlane();
Carrier carrier = new Carrier(10, 2);
carrier.travelInfo(bus);
Carrier carrier2 = new Carrier(4, 8);
carrier2.travelInfo(taxi);
Carrier carrier3 = new Carrier(162, 720);
carrier3.travelInfo(pp);
}
}
In the code above for the Carrier
class we are using the PublicTransport
interface type in our parameter lists for the methods in the class. In the TestPolyIntArgs
test class we are
passing across the argument types polymorphically using the PublicTransport
reference type. Save and compile the Carrier
and TestPolyIntArgs
classes and then run the
TestPolyIntArgs
class in directory c:\_OOConcepts in the usual way.

The above screenshot shows the output of running our TestPolyIntArgs
class. We are passing across objects from totally different hierarchies using the PublicTransport
interface type as the
reference type. The JVM
uses polymorphic method invocation to dynamically invoke the correct queue()
and payFare()
methods based on the actual
object type within the Carrier
class methods at runtime.
Polymorphic Interface Return Types Top
We can also return objects from methods using the interface type as the reference type, safe in the knowledge that the correct method will be invoked on the actual object at runtime.
package info.java8;
/*
Test class for Polymorphic Interface Type Return
*/
public class PolymorphicIntReturn {
public static void main (String[] args) {
// Array declaration below is creating an Array object so can use interface supertype
PublicTransport[] pt = new PublicTransport[3];
for (int i=0; i < 3; i++) {
pt[i] = createPaidTransport(i);
pt[i].queue(i);
pt[i].payFare(i);
}
}
/*
Create a subtype from the Vehicle or Plane hierarchy and
return the PublicTransport supertype
*/
static PublicTransport createPaidTransport (int i) {
PublicTransport pt;
switch (i) {
case 0:
pt = new Bus();
break;
case 1:
pt = new Taxi();
break;
default:
pt = new PassengerPlane();
}
return pt;
}
}
In the code above we are instantiating objects from totally different hierachies using the PublicTransport
interface type as the reference type. Save, compile and run the PolymorphicIntReturn
in
directory c:\_OOConcepts in the usual way.

The above screenshot shows the output of running our slightly contrived PolymorphicIntReturn
test class. We are returning the PublicTransport
interface type from the createPaidTransport()
method. The JVM
uses polymorphic method invocation to dynamically invoke the correct queue()
and payFare()
methods based on the actual object type on return.
For more on polymorphism and how it works take a look at the Polymorphism lesson.
Using instanceof
With Interfaces Top
We first looked at the instanceof
operator in the Reference Variables lesson. Now we have learnt
about polymorphic types and interfaces it's time to take another look at this operator and its application. The following program uses the Vehicle
hierarchy and the PublicTransport
interface to
show some instanceof
usages:
package info.java8;
/*
A Minibus Class
*/
public class Minibus extends Bus {
public static void main(String[] args) {
Vehicle bus = new Bus();
checkType(bus);
Vehicle car = new Car();
checkType(car);
Vehicle minibus = new Minibus();
checkType(minibus);
PassengerPlane pp = new PassengerPlane();
checkType(pp);
Vehicle taxi = new Taxi();
checkType(taxi);
}
public static void checkType(Object o) {
if (o instanceof Minibus) {
((Minibus)o).MinibusClassOnlyMethod(); // Downcast to minibus
}
if (o instanceof PublicTransport ) {
/*
Downcast argument to PublicTransport interface type
*/
PublicTransportOnlyMethod(((PublicTransport)o));
}
}
private static void MinibusClassOnlyMethod() {
System.out.println("We are in the MinibusClassOnlyMethod().");
System.out.println("We have safely downcast to the Minibus class to get here.");
}
private static void PublicTransportOnlyMethod(PublicTransport pt) {
pt.queue(10);
pt.payFare(2);
}
}
In the code above we are instantiating objects from two different hierachies. We call the checkType(Object o)
which uses The Object
Superclass as a parameter. This enables us to
pass any argument to the method. Within the method we firstly use the instanceof
operator to test for a Minubus
type and if so we print some messages for that type. We then check to see if
this type is an instance of the PublicTransport
interface. If it is we cast the object argument to the PublicTransport
interface type and pass this to the PublicTransportOnlyMethod(PublicTransport pt)
method. Within this method the overridden methods for the appropriate class are called. Save, compile and run the Minibus
in directory c:\_OOConcepts in the usual way.

The above screenshot shows the output of running our Minibus
test class. If you look closely at the output you will see that the queue()
and payFare()
methods are output twice
for the Bus
class and yet we only made one occurrence of Bus
. The second lot of output is actually for the Minibus
class. The reason for this is inheritance; if a superclass
implements an interface, then a subclass will have an implicit implementation through the superclass.
Related Quiz
OO Concepts Quiz 15 - Method References
Lesson 15 Complete
In this lesson we looked at method references, which were introduced in Java8, their syntax and how to use them.
What's Next?
In the first of three lessons on nested classes we look at nested static member classes.