Method ReferencesJ8 Home « Method References

In the last lesson we introduced Lambda Expressions to create a block of code that is available to process without executing it. Lambdas allow us to write clear concise code and remove code bloat. In this lesson we look at method references which are a special type of lambda expression used to create simple lambdas by referencing existing methods.

There are four types of method references:

  1. Reference to instance method of a particular object - someObject::instanceMethod
  2. Reference to static method - someClass::staticMethod
  3. Reference to arbitrary object of particular type - someClass::instanceMethod
  4. Constructor Reference - someObject::new

We will look at examples of each type of method reference in the following sections and then discuss how to extract this and super via method references.

Reference to Instance Method of a Particular Object  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.


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.

run  Bus class implementation

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.

Reference to Static Method 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:

interface 6 interface 7 interface 8

Press the button below to cycle through our Vehicle Town slideshow.


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.

run PassengerPlane Test

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.

Reference to Arbitrary Object of a Particular Type 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.

run PassengerPlane Test

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.

Using this and super 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.

run Minibus Class

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.