Monday, June 09, 2014

What's New in Java 8 - Method References

Method references provide easy-to-read lambda expressions for methods that already have a name.
We have seen lambda expressions to crate anonymous methods in my post What's New in Java 8 - Lambda Expressions. Sometimes a lambda expression does nothing but call an existing method (will see example in detail). In those cases, we use method references instead lambda expressions. Method references are compact and easy to read lambda expressions for methods that already have a name.

Example  :

To better understand, consider the same car example discussed in my previous post, but here a extra compareByEngineCC static method.

public class Car{
    public enum Color {
        BLACK, WHITE, RED
    }
    public String modelName;
    public Color color;
    public int engineCC;
    public void printModelName() {
        // ...
    }
    
    public static int compareByEngineCC(Car c1, Car c2) {
        if( c1.engineCC  == c2.engineCC) return 0;
        return c1.engineCC > c2.engineCC?1:-1;
    }
}

The requirement is sort the array of cars by engineCC, so that I added a static method to  compare the cars.
We can use sort method which is static in Arrays class to meat this requirement, but in order to use this static method we need to provide Comparator, So we implemented a CarEngineCCComparator class as shown below


public class CarEngineCCComparator implements  Comparator<Car> {
@Override
public int compare(Car c1, Car c2) {
return Car.compareByEngineCC(c1, c2);
}
}

and assume that array of cars is Car[] cars to be sorted.then by calling static method of Arrays class will get the sorted array 

 Arrays.sort(cars,new CarEngineCCComparator() ); 

If you observer closely, the interface Comparator  is functional interface(a interface with only one abstract method), that means we can use lambda expression instead implement a new class. then our method call will be 

 Arrays.sort(cars,(c1,c2)->Car.compareByEngineCC(c1,c2)); 

Here a  lambda expression does nothing but call an existing method, so in this case we use method references instead lambda expression as follows. 

Arrays.sort(cars, Car::compareByEngineCC);

The method reference Car::compareByEngineCC is semantically the same as the lambda expression
 (c1,c2)->Car.compareByEngineCC(c1,c2). 

Each has the following characteristics:
  • Its formal parameter list is copied from Comparator<Car>.compare, which is (Car, Car).
  • Its body calls the method Car.compareByEngineCC.

 Kinds of Method References:

There are four kinds of method references:

KindExample
Reference to a static methodContainingClass::staticMethodName
Reference to an instance method of a particular objectContainingObject::instanceMethodName
Reference to an instance method of an arbitrary object of a particular typeContainingType::methodName
Reference to a constructorClassName::new

Reference to a Static Method

The method reference Car::compareByEngineCC is a reference to a static method.


Reference to an Instance Method of a Particular Object

The following is an example of a reference to an instance method of a particular object:

class ComparisonProvider{ 
     public int compareByEngineCC(Car c1, Car c2) {
         if( c1.engineCC == c2.engineCC) return 0;
         return c1.engineCC > c2.engineCC?1:-1; 
    } 
}
ComparisonProvider myComparisonProvider = new ComparisonProvider(); Arrays.sort(cars,myComparisonProvider::compareByEngineCC);

The method reference myComparisonProvider::compareByEngineCC invokes the method compareByEngineCC that is part of the object myComparisonProvider. The JRE infers the method type arguments, which in this case are (Car, Car).

Reference to an Instance Method of an Arbitrary Object of a Particular Type

The following is an example of a reference to an instance method of an arbitrary object of a particular type:

String[] stringArray = { "Alice", "Bob", "Erin", "Dave ", , "Robert", "Michael", "Linda" }; Arrays.sort(stringArray, String::compareToIgnoreCase);

The equivalent lambda expression for the method reference String::compareToIgnoreCase would have the formal parameter list (String a, String b), where a and b are arbitrary names used to better describe this example. The method reference would invoke the method a.compareToIgnoreCase(b).


Reference to a Constructor

We can reference a constructor in the same way as a static method by using the name new. The following method copies elements from one collection to another:

public static <T, SOURCE extends Collection<T>, DEST extends Collection<T>>
      DEST transferElements(SOURCE sourceCollection,
                                               Supplier<DEST> collectionFactory) {
                                               DEST result = collectionFactory.get();
                                              for (T t : sourceCollection) {
                                                    result.add(t);
                                            }
                                          return result;
       }

The functional interface Supplier contains one method get that takes no arguments and returns an object. Consequently, you can invoke the method transferElements with a lambda expression as follows:
Set<Car> transformedSetLambda = transferElements(cars, () -> { return new HashSet<>(); });

You can use a constructor reference in place of the lambda expression as follows:

Set<Car> transformedSet = transferElements(roster, HashSet::new);

The Java compiler infers that you want to create a HashSet collection that contains elements of type Car. Alternatively, you can specify this as follows:

Set<Car> transformedSet = transferElements(roster, HashSet<Car>::new);

More new features of the java 8 will be discussed in my next posts. Thank you for reading this post, please post you suggestions and comments on this.

No comments:

Post a Comment

Distributed Transactions

What is a distributed transaction?  Transactions that span over multiple physical systems or computers over the network, are simply termed D...