Method References
This is an important feature of lambda expressions where we refer to a method without executing it.
It relates to lambda expressions because as it requires a target type context that consists of a compatible functional interface.
When evaluated, a method reference also creates an instance of the functional interface.
There are different types of method references as follows
Method References to static Methods
To create a static method reference, the general syntax is:
ClassName::methodName
::-This two colon operator is introduced in JDK8 for this purpose only.
- This method reference can be used anywhere in which it is compatible with its target type.
// Demonstrate a method reference for a static method.
// A functional interface for string operations.
interface DoubleFunc
{
double func(double n);
}
// This class defines a static method called strReverse().
class MyDoubleOps
{
// A static method that reverses a string.
static double getValue(double d)
{
return d;
}
}
public class MethodRefDemo
{
// This method has a functional interface as the type of
// its first parameter. Thus, it can be passed any instance
// of that interface, including a method reference.
static double doubleOp(DoubleFunc df, double i)
{
return df.func(i);
}
public static void main(String args[])
{
double di=324.346;
// Here, a method reference to strReverse is passed to stringOp().
double out = doubleOp(MyDoubleOps::getValue,di );
System.out.println("i value is " + out);
}
}
Output:
i value is 324.346
// Demonstrate a method reference for a static method.
// A functional interface for string operations.
interface StringFunc
{
String func(String n);
}
// This class defines a static method called strReverse().
class MyStringOps
{
// A static method that reverses a string.
static String strReverse(String str)
{
String result = "";
int i;
for(i = str.length()-1; i >= 0; i--)
result += str.charAt(i);
return result;
}
}
public class MethodRefDemo
{
// This method has a functional interface as the type of
// its first parameter. Thus, it can be passed any instance
// of that interface, including a method reference.
static String stringOp(StringFunc sf, String s)
{
return sf.func(s);
}
public static void main(String args[])
{
String inStr = "Lambdas add power to Java";
String outStr;
// Here, a method reference to strReverse is passed to stringOp().
outStr = stringOp(MyStringOps::strReverse, inStr);
System.out.println("Original string: " + inStr);
System.out.println("String reversed: " + outStr);
}
}
Output:
Original string: Lambdas add power to Java
String reversed: avaJ ot rewop dda sadbmaL
Method References to Instance Methods
- we can make a reference to an instance method on a specific object, by using the following basicsyntax:
objRef::methodName
- This is same as static method reference but instead of ClassName we uses object reference.
// Demonstrate a method reference for a static method.
// A functional interface for string operations.
interface DoubleFunc
{
double func(double n);
}
// This class defines a static method called strReverse().
class MyDoubleOps
{
// An instance method that reverses a string.
double getValue(double d)
{
return d;
}
}
public class MethodRefDemo
{
// This method has a functional interface as the type of
// its first parameter. Thus, it can be passed any instance
// of that interface, including a method reference.
static double doubleOp(DoubleFunc df, double i)
{
return df.func(i);
}
public static void main(String args[])
{
double di=324.346;
//creating object reference to the MyDoubleOps class
MyDoubleOps MyDoubleOpsReference = new MyDoubleOps();
// Here, a method reference to strReverse is passed to stringOp().
double out = doubleOp(MyDoubleOpsReference::getValue,di );
System.out.println("i value is " + out);
}
}
Output:
i value is 324.346
Here getValue() method called with the reference of object MyDoubleOpsReference
- We can specify an instance method that can be used by any object of a given class not just a specified object by the following syntax:
ClassName::instanceMethodName
Here, the name of the class is used instead of a specific object, even though an instance method is specified.
- In this the first parameter of the functional interface matches the invoking object and the second parameter matches the parameter specified by the method.
// Use an instance method reference with different objects.
// A functional interface that takes two reference arguments
// and returns a boolean result.
interface MyFunc<T>
{
boolean func(T v1, T v2);
}
// A class that stores the temperature high for a day.
class HighTemp
{
private int hTemp;
HighTemp(int ht)
{
hTemp = ht;
}
// Return true if the invoking HighTemp object has the same temperature as ht2.
boolean sameTemp(HighTemp ht2)
{
return hTemp == ht2.hTemp;
}
// Return true if the invoking HighTemp object has a temperature that is less than ht2.
boolean lessThanTemp(HighTemp ht2)
{
return hTemp < ht2.hTemp;
}
}
public class InstanceMethWithObjectRefDemo
{
// A method that returns the number of occurrences
// of an object for which some criteria, as specified by
// the MyFunc parameter, is true.
static <T> int counter(T[] vals, MyFunc<T> f, T v)
{
int count = 0;
for(int i=0; i < vals.length; i++)
if(f.func(vals[i], v))
count++;
return count;
}
public static void main(String args[])
{
int count;
// Create an array of HighTemp objects.
HighTemp[] weekDayHighs = { new HighTemp(89), new HighTemp(82),
new HighTemp(90), new HighTemp(89),
new HighTemp(89), new HighTemp(19),
new HighTemp(12), new HighTemp(83) };
// Use counter() with arrays of the class HighTemp.
// Notice that a reference to the instance method
// sameTemp() is passed as the second argument.
count = counter(weekDayHighs, HighTemp::sameTemp, new HighTemp(89));
System.out.println(count + " days had a high of 89");
// Now, use lessThanTemp() to find days when temperature was less than a specified value.
count = counter(weekDayHighs, HighTemp::lessThanTemp, new HighTemp(89));
System.out.println(count + " days had a high less than 89");
}
}
Output:
3 days had a high of 89
4 days had a high less than 89
- Each method has a parameter of type HighTemp and each method returns a boolean result.
Thus, each is compatible with the MyFunc functional interface because the invoking object type can be mapped to the first parameter of func( ) and the argument mapped to func( )’s second parameter.
we can refer to the superclass version of a method by use of super, as shown here:
super::name
- The name of the method is specified by name.
Method References with Generics
- We can use method reference with the Generic classes and Generic methods.
// Demonstrate a method reference to a generic method
// declared inside a non-generic class.
// A functional interface that operates on an array and a value, and returns an int result.
interface MyFunc<T>
{
int func(T[] vals, T v);
}
// This class defines a method called countMatching() that
// returns the number of items in an array that are equal to a specified value.
//Notice that countMatching() is generic, but MyArrayOps is not.
class MyArrayOps
{
static <T> int countMatching(T[] vals, T v)
{
int count = 0;
for(int i=0; i < vals.length; i++)
if(vals[i] == v)
count++;
return count;
}
}
public class GenericMethodRefDemo
{
// This method has the MyFunc functional interface as the type of its first parameter. The other two parameters
// receive an array and a value, both of type T.
static <T> int myOp(MyFunc<T> f, T[] vals, T v)
{
return f.func(vals, v);
}
public static void main(String args[])
{
Integer[] vals = { 1, 2, 3, 4, 2, 3, 4, 4, 5 };
String[] strs = { "One", "Two", "Three", "Two" };
int count;
//static method reference
count = myOp(MyArrayOps::<Integer>countMatching, vals, 4); //<Integer> should be followed by ::
System.out.println("vals contains " + count + " 4s");
count = myOp(MyArrayOps::<String>countMatching, strs, "Two");
System.out.println("strs contains " + count + " Twos");
}
}
Output:
vals contains 3 4s
strs contains 2 Twos
- Before to use max() method an instance of Comparator had to be obtained by first explicitly implementing it by a class, and then creating an instance of that class.
- This instance was then passed as the comparator to max( ).
- By use of the method reference we can solve this problem by simply pass a reference to a comparison method to max( ) because doing so automatically implements the comparator.
// Use a method reference to help find the maximum value in a collection.
import java.util.*;
class MyClass {
private int val;
MyClass(int v) { val = v; }
int getVal() { return val; }
}
class UseMethodRef {
// A compare() method compatible with the one defined by Comparator<T>.
static int compareMC(MyClass a, MyClass b) {
return a.getVal() - b.getVal();
}
public static void main(String args[])
{
ArrayList<MyClass> al = new ArrayList<MyClass>();
al.add(new MyClass(1));
al.add(new MyClass(4));
al.add(new MyClass(2));
al.add(new MyClass(9));
al.add(new MyClass(3));
al.add(new MyClass(7));
// Find the maximum value in al using the compareMC() method.
MyClass maxValObj = Collections.max(al, UseMethodRef::compareMC);
System.out.println("Maximum value is: " + maxValObj.getVal());
}
}
Output:
Maximum value is: 30
Constructor References
- We can create a reference to a constructor same as method with the following syntax:
classname::new
- This reference can be assigned to any functional interface reference that have method which have type compatible with the constructor.
interface Messageable{
Message getMessage(String msg);
}
class Message{
public Message(String msg){
System.out.print(msg);
}
}
public class ConstructorReference {
public static void main(String[] args) {
Messageable hello = Message::new;
hello.getMessage("Hello");
}
}
Output:
Hello
- We can refer a constructors which are refer to Generic classes by simply specifying the type argument after the class name
// Demonstrate a constructor reference with a generic class.
// MyFunc is now a generic functional interface.
interface MyFunc<T>
{
MyClass<T> func(T n);
}
class MyClass<T>
{
private T val;
// A constructor that takes an argument.
MyClass(T v)
{
val = v;
}
// This is the default constructor.
MyClass( )
{
val = null;
}
T getVal()
{
return val;
}
}
public class ConstructorRefDemo2
{
public static void main(String args[])
{
// Create a reference to the MyClass<T> constructor by specifying type argument after class name.
MyFunc<Integer> myClassCons = MyClass<Integer>::new;
// Create an instance of MyClass<T> via that constructor reference.
MyClass<Integer> mc = myClassCons.func(100);
// Use the instance of MyClass<T> just created.
System.out.println("val in mc is " + mc.getVal( ));
// Create a reference to the MyClass<T> constructor by specifying type argument String after class name.
MyFunc<String> myClassCons1 = MyClass<String>::new;
// Create an instance of MyClass<T> via that constructor reference.
MyClass<String> mc1 = myClassCons1.func("Hello");
// Use the instance of MyClass<T> just created.
System.out.println("val in mc1 is " + mc1.getVal( ));
}
}
Output:
val in mc is 100
val in mc1 is Hello
- We can create a constructor reference for an array by using following syntax:
type[]::new
Here,type specifies the type of object being created
interface MyArrayCreator<T>
{
T func(int n);
}
class MyClass
{
private int val;
MyClass(int v) { val = v; }
int getVal() { return val; }
}
public class Demo
{
public static void main(String argts[])
{
MyArrayCreator<MyClass[]> mcArrayCons = MyClass[]::new;
MyClass[] a = mcArrayCons.func(2);
a[0] = new MyClass(1);
a[1] = new MyClass(2);
System.out.println(a[0].getVal());
System.out.println(a[1].getVal());
}
}
Output:
1
2
Predefined Functional Interfaces
- JDK 8 adds a new package called java.util.function that provides several predefined ones. | Interface | Purpose | | :--- | :--- | | UnaryOperator<T> | Apply a unary operation to an object of type T and return the reult, which is also of type T. Its method is called apply(). | | BinaryOperator<T> | Apply an operation to two objects of type T and return the result, which is also of type T. Its method is called apply(). | | Consumer<T> | Apply an operation on an object of type T. It is method is called accept(). | | Supplier<T> | Return an object of type T. Its method is called get(). | | Function<T,R> | Apply an operation to an object of type T and return the result as an object of type R. Its method is called apply(). | | Predicate<T> | Determine if an object of type T fulfills some constraint. Return a boolean value that indicates the outcome. Its method is called test(). |
// Use the Function built-in functional interface.
// Import the Function interface.
import java.util.function.Function;
public class UseFunctionInterfaceDemo
{
public static void main(String args[])
{
// This block lambda computes the factorial of an int value.
// This time, Function is the functional interface.
Function<Integer, Integer> factorial = (n) ->
{
int result = 1;
for(int i=1; i <= n; i++)
result = i * result;
return result;
};
System.out.println("The factoral of 3 is " + factorial.apply(3));
System.out.println("The factoral of 5 is " + factorial.apply(5));
}
}
Output:
The factoral of 3 is 6
The factoral of 5 is 120