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

results matching ""

    No results matching ""