Java. Types of method references. Reference to methods




Types of method references. Reference to static method. Reference to instance method


Contents


Search other websites:

1. References to methods. Associating lambda expressions with method references. Types of method references

If lambda expressions bind to a functional interface, then methods can also bind to a functional interface. A method is associated with a functional interface using a method reference. If a lambda expression can be passed to some method as a parameter, then a method reference can also be passed as a parameter. Using this reference, you can access the method without calling it.

There are 4 types of method references in Java:

  • references to static methods;
  • references to instance methods;
  • references to constructors;
  • references to generic (template) methods.

 

2. Reference to static methods
2.1. General form of references to static methods. Delimiter ::

In Java, you can declare static method references. A reference to a static method can be passed to some method and used there to call a static method.

The general form of declaring a reference to a static method is as follows:

class_name::method_name

The :: delimiter was introduced in JDK 8 to define a method reference.

For example. If class A declares a static method named Method()

class A {

  // ...

  static return_type Method(parameters) {
    // ...
  }
}

then the reference to this method will be as follows

A::Method

 

2.2. Examples of references to static methods
2.2.1. Reference to the static method for calculating the volume of a sphere

Task. Develop a static method that takes the radius of a sphere and calculates it’s volume. Implement the passing of the method for calculating the volume of a sphere to another method as a parameter. Test the method.

Solution. When solving this problem, you need to develop the following elements:

  • functional interface ICalcFigure. The interface declares a single method, Volume(), which receives a double number and returns a double value;
  • the CalcVolume() class, which declares the static SphereVolume() method. The method gets the radius of the ball (double) and returns the volume of the ball (double);
  • the FigureOperation class, which declares the Volume() method. This method takes two parameters. The first parameter is a reference to the ICalcFigure interface. The second parameter is the radius of the circle. By reference to ICalcFigure, a static method for calculating the volume of a sphere will be passed.

The text of the program for solving this problem is as follows.

// Reference to a static method that calculates the volume of a sphere
// 1. A functional interface that defines a method
//   that takes a double as a parameter and returns a double result
interface ICalcFigure {
  double Volume(double radius);
}

// 2. A class that defines a static method for calculating the volume of a sphere
class CalcVolume {
  static double SphereVolume(double radius) {
    // return the volume of a sphere
    return 4.0/3.0*Math.PI*radius*radius*radius;
  }
}

// 3. The class in which the method is defined that receives a reference to the functional interface
class FigureOperation {
  double Volume(ICalcFigure ref, double radius) {
    return ref.Volume(radius);
  }
}

// 4. Class demonstrating the use of a static method reference
public class RefMethod {

  public static void main(String[] args) {
    // Calculate the volume of a sphere
    // 1. Declare an instance of the FigureOperation class
    FigureOperation fo = new FigureOperation();

    // 2. Call the Volume method and pass it a reference to the static method
    //    Calculate the volume of a sphere of radius 5.0
    double volume = fo.Volume(CalcVolume::SphereVolume, 5.0);

    // 3. Вывести результат
    System.out.println("volume = " + volume);
  }
}

The result of the program

volume = 523.5987755982989

 

2.2.2. Reference to generalized static method for reversing an array of numbers

Task. Develop a generic static ReverseNumbers() method that implements reversing an array of numbers. Implement passing a method to another method by reference.

Solution. The sequence of steps to be followed to solve the problem:

  • declare a generic (template) functional interface IArrayFunction. The interface must define the Function() method, which receives an array of numbers as a parameter and returns an array of numbers (using the return statement);
  • declare the ProcessArray class with a generic static method ProcessArrayReverse();
  • declare the DemoRefMethods class in which to implement the template method DemoReverse(). In the DemoReverse() method, use the IArrayFunction functional interface reference to call the Function() method;
  • in the main() function, demonstrate passing the static ProcessArrayReverse() method to the DemoReverse() method.

The program text that solves this problem is as follows

// Reference to static method
// 1. Generalized functional interface
interface IArrayFunction<T extends Number> {
  // A method that receives an array of type T and returns an array of type T
  T[] Function(T[] array);
}

// 2. A class that contains a generic static method that returns an array of numbers
class ProcessArray {
  public static <T extends Number> T[] ProcessArrayReverse(T[] array) {
    T tmp;
    for (int i=0; i<array.length/2; i++) {
      tmp = array[i];
      array[i] = array[array.length - 1 - i];
      array[array.length - 1 - i] = tmp;
    }
    return array;
  }
}

// 3. The class in which the method is defined that receives a reference to the functional interface
class DemoRefMethods {
  // A method that receives a reference to the functional interface IArrayFunction<T>
  <T extends Number> T[] DemoReverse(IArrayFunction<T> ref, T[] array) {
    return ref.Function(array);
  }
}

// 4. Class demonstrating the use of a reference to static method
public class RefMethod {

  public static void main(String[] args) {
    // Implement reversing an array of integers
    // 1. Declare an instance of the DemoRefMethods class
    DemoRefMethods obj = new DemoRefMethods();

    // 2. Testing array
    Integer AI[] = { 1, 3, 5, 8, 4, 2 };
    System.out.println("Array AI: ");
    for (int i=0; i<AI.length; i++)
      System.out.print(" " + AI[i]);
    System.out.println();

    // 3. Pass static method ProcessArray.ProcessArrayReverse() to obj.DemoReverse() method
    Integer AI2[] = obj.DemoReverse(ProcessArray::ProcessArrayReverse, AI);

    // 4. Print the result - array AI2
    System.out.println("Array AI2: ");
    for (int i=0; i<AI2.length; i++)
      System.out.print(" " + AI2[i]);
    System.out.println();
  }
}

The result of the program

Array AI:
1 3 5 8 4 2
Array AI2:
2 4 8 5 3 1

 

3. Reference to instance methods
3.1. The general form of a reference to the instance methods

One of two general forms is used to declare a reference to the instance method:

instance_name::method_name
class_name::method_name

Otherwise, an instance method is specified alongside any object of the given class, not just the specified object.

 

3.2. Examples of using instance method references
3.2.1. Reference to methods that perform actions on complex numbers

Task. Demonstrate passing to a certain method of a class of methods for summing and subtracting complex numbers.

Solution. To solve the problem in the program, it is necessary to develop the following elements (components).

1. Class Complex that defines a complex number

class Complex {
  // Real and imaginary parts of a complex number
  public double re;
  public double im;
  ...
}

2. Functional interface for operating the Complex type

interface IFunction {
  // A method that operates on complex numbers of type Complex
  Complex Function(Complex c1, Complex c2);
}

The Function() method is declared in the functional interface. The method signature specifies the receiving of two complex numbers and return a complex number. Such a signature can be suitable for operations of addition, subtraction, multiplication, division of complex numbers, and other operations.

3. A class that contains the actual methods for processing complex numbers (summation, subtraction).

class ProcessComplex {
  // The summation of complex numbers
  Complex Add(Complex c1, Complex c2) {
    ...
  }

  // Subtraction of complex numbers
  Complex Sub(Complex c1, Complex c2) {
    ...
  }
}

4. A class containing a method that receives a reference to an instance method. In our case, you need to pass the Add () and Sub() methods of an instance of the ProcessComplex class.

class DemoRefMethods {
  // This method will be passed a reference to the Add() and Sub()
  // methods of an instance of the ProcessComplex class.
  Complex RefMethod(IFunction ref, Complex c1, Complex c2) {
    ...
  }
}

5. Class that demonstrates the use of an instance method reference

// 5. A class that demonstrates the use of an instance method reference
public class RefMethod {
  public static void main(String[] args) {
    ...
  }
}

Below is the text of the solution to the task.

// Reference to the instance method
// 1. Class that defines a complex number
class Complex {
  // Real and imaginary parts of a complex number
  public double re;
  public double im;

  // Constructor
  public Complex(double _re, double _im) {
    re = _re;
    im = _im;
  }

  // Method that prints a complex number
  void Print(String text) {
    System.out.print(text);
    System.out.print(re);
    if (im>0)
      System.out.print("+");
    System.out.println(im);
  }
}

// 2. A functional interface that operates on the Complex type
interface IFunction {
  // Method operating on complex numbers of type Complex
  Complex Function(Complex c1, Complex c2);
}

// 3. A class that contains methods for handling complex numbers
class ProcessComplex {
  // Summing complex numbers
  Complex Add(Complex c1, Complex c2) {
    Complex c3 = new Complex(c1.re+c2.re, c1.im+c2.im);
    return c3;
  }

  // Subtraction of complex numbers
  Complex Sub(Complex c1, Complex c2) {
    Complex c3 = new Complex(c1.re-c2.re, c1.im-c2.im);
    return c3;
  }
}

// 4. The class in which the method is defined, which receives
//   a reference to the functional interface IFunction
class DemoRefMethods {
  // A method that gets complex numbers and a reference
  // to the functional interface IFunction.
  // This method will be passed a reference to the method
  // of an instance of the ProcessComplex class.
  Complex RefMethod(IFunction ref, Complex c1, Complex c2) {
    Complex c3;
    c3 = ref.Function(c1, c2);
    return c3;
  }
}

// 5. Class that demonstrates the use of an instance method reference
public class RefMethod {

  public static void main(String[] args) {
    // Implement summation and subtraction of complex numbers
    // 1. Declare the instance of ProcessComplex Class
    ProcessComplex obj1 = new ProcessComplex();

    // 2. Declare an instance of the DemoRefMethods class
    DemoRefMethods objDemo = new DemoRefMethods();

    // 3. Create complex numbers for testing
    Complex c1 = new Complex(5, -8);
    Complex c2 = new Complex(3, 4);
    c1.Print("c1 = ");
    c2.Print("c2 = ");

    // 4. Pass to the objDemo.RefMethod() method a reference
    //   to the obj1.Add () method and display the result
    Complex c3 = objDemo.RefMethod(obj1::Add, c1, c2);
    c3.Print("c3 = c1 + c2 = ");

    // 5. Demonstrate subtraction of complex numbers
    Complex c4;
    // pass obj1::Sub to RefMethod()
    c4 = objDemo.RefMethod(obj1::Sub, c1, c2);
    c4.Print("c4 = c1 - c2 = ");
  }
}

The result of the program

c1 = 5.0-8.0
c2 = 3.0+4.0
c3 = c1 + c2 = 8.0-4.0
c4 = c1 - c2 = 2.0-12.0

 

3.2.2. A reference to the templated method of instance.

Task. Implement a class containing the following generic (template) methods for working with arrays:

  • calculating the sum of array elements;
  • calculation the average of array.

Demonstrate calling array processing methods using the method of passing a reference to an instance method as a parameter.

Solution.

// Reference to the generic instance method
// 1. Generalized (templated) functional interface
interface IFuncArray<T extends Number> {
  // A method that receives an array of type T and returns a double
  double FuncArray(T[] array);
}

// 2. A class containing methods for processing arrays
class ProcessArray<T extends Number> {
  // The sum of array items
  double Sum(T[] array) {
    double sum = 0;
    for (T value : array)
      sum += value.doubleValue();
    return sum;
  }

  // The average of array items
  double Avg(T[] array) {
    double avg = 0;
    for (T value : array)
      avg += value.doubleValue();
    return avg/array.length;
  }
}

// 3. A class that contains a templated method
//   that returns a reference to the instance method
class ClassOperation {
  <T extends Number> double Operation(IFuncArray<T> ref, T[] array) {
    // invoke method of IFuncArray interface
    double result = ref.FuncArray(array);
    return result;
  }
}

// 4. A class that demonstrates a reference to the instance method
public class RefMethod {

  public static void main(String[] args) {
    // 1. Declare the instance of ClassOperation class
    ClassOperation Op = new ClassOperation();

    // 2. Declare a reference to ProcessArray<T> class
    ProcessArray<Integer> Array = new ProcessArray<Integer>();

    // 3. Prepare an array of integers for processing and display it
    Integer[] AI = { 2, 4, 8, -1, 3, -5 };
    System.out.print("AI = ");
    for (int t : AI)
      System.out.print(t + " ");
    System.out.println();

    // 4. Calculate the arithmetic average of an AI
    //    by passing Array.Avg() to Op.Operation ()
    double average = Op.Operation(Array::Avg, AI);
    System.out.println("average = " + average);

    // 5. Calculate the sum of array AI items.
    //    To do this, you need to pass the Array.Sum() method to Op.Operation()
    double sum = Op.Operation(Array::Sum, AI);
    System.out.println("sum = " + sum);
  }
}

As you can see from the main() function code, the Avg() and Sum() methods of the Array instance are passed by calling the only Op.Operation() method.

...
double average = Op.Operation(Array::Avg, AI);
...
double sum = Op.Operation(Array::Sum, AI);
...

The difference in calls is in passing the first parameter, which is a reference to the instance method. In the first case, an instance method is passed that calculates the arithmetic average using the following syntax

Array::Avg

Otherwise, an instance method is passed

Array::Sum

which calculates the sum.

After running for execution, the program will print the following result

AI = 2 4 8 -1 3 -5
average = 1.8333333333333333
sum = 11.0

 


Related topics