Java. Ссылки на методы. Виды ссылок на методы. Ссылка на статический метод. Ссылка на метод экземпляра




Ссылки на методы. Виды ссылок на методы. Ссылка на статический метод. Ссылка на метод экземпляра


Содержание


Поиск на других ресурсах:

1. Ссылки на методы. Связь лямбда-выражений со ссылками на методы. Виды ссылок на методы

Если лямбда-выражения связываются с функциональным интерфейсом, то методы также могут быть связаны с функциональным интерфейсом. Связь метода с функциональным интерфейсом осуществляется с помощью ссылки на метод.

Если лямбда-выражение может быть передано в некоторый метод как параметр, то ссылка на метод также может быть передана в качестве параметра. С помощью этой ссылки можно обращаться к методу не вызывая его.

В Java различают 4 вида ссылок на методы:

  • ссылки на статические методы;
  • ссылки на методы экземпляра;
  • ссылки на конструкторы;
  • ссылки на обобщенные (шаблонные) методы.

 

2. Ссылка на статические методы
2.1. Общая форма ссылок на статические методы. Разделитель ::

В языке Java можно объявлять ссылки на статические методы. Ссылка на статический метод может быть передана в некоторый метод и там использована для вызова статического метода.

Общая форма объявления ссылки на статический метод следующая:

имя_класса::имя_метода

Разделитель :: внедрен в версии JDK 8 для определения ссылки на метод.

Например. Если в классе A объявлен статический метод с именем Method()

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

то ссылка на этот метод будет следующей

A::Method

 

2.2. Примеры ссылок на статические методы
2.2.1. Ссылка на статический метод вычисления объема шара

Условие задачи. Разработать статический метод, который получает радиус шара и вычисляет объем шара. Реализовать передачу метода вычисления объема шара в другой метод в качестве параметра. Провести тестирование метода.

Решение. При решении данной задачи нужно разработать следующие элементы:

  • функциональный интерфейс ICalcFigure. В интерфейсе объявляется единственный метод Volume(), который получает число типа double и возвращает значение типа double;
  • класс CalcVolume(), в котором объявляется статический метод SphereVolume(). Метод получает радиус шара (double) и возвращает объем шара (double)
  • класс FigureOperation, в котором объявляется метод Volume(). Этот метод получает два параметра. Первый параметр является ссылкой на интерфейс ICalcFigure. Второй параметр — радиус окружности. По ссылке на ICalcFigure будет передан статический метод вычисления объема шара.

Текст программы, решения данной задачи, следующий.

// Ссылка на статический метод, вычисляющий объем шара
// 1. Функциональный интерфейс, определяющий метод, который получает параметром
//   число типа double и возвращает результат типа double
interface ICalcFigure {
  double Volume(double radius);
}

// 2. Класс, в котором определен статический метод вычисления объема шара
class CalcVolume {
  static double SphereVolume(double radius) {
    // повернути об'єм кулі
    return 4.0/3.0*Math.PI*radius*radius*radius;
  }
}

// 3. Класс, в котором определен метод, получающий ссылку на функциональный интерфейс
class FigureOperation {
  double Volume(ICalcFigure ref, double radius) {
    return ref.Volume(radius);
  }
}

// 4. Класс, демонстрирущий использование ссылки на статический метод
public class RefMethod {

  public static void main(String[] args) {
    // Вычислить объем шара
    // 1. Объявить экземпляр класса FigureOperation
    FigureOperation fo = new FigureOperation();

    // 2. Вызвать метод Volume и передать ему ссылку на статический метод
    //    Вычислить объем шара радиуса 5.0
    double volume = fo.Volume(CalcVolume::SphereVolume, 5.0);

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

Результат выполнения программы

volume = 523.5987755982989

 

2.2.2. Ссылка на обобщенный статический метод реверсирования массива чисел

Условие задачи. Разработать обобщенный статический метод ReverseNumbers, который реализует реверсирование массива чисел. Реализовать передачу метода в другой метод по ссылке.

Решение. Последовательность шагов, которые нужно выполнить чтобы решить задачу:

  • объявить обобщенный (шаблонный) функциональный интерфейс IArrayFunction. В интерфейсе должен быть определен метод Function(), который получает параметром массив чисел и возвращает массив чисел (с помощью оператора return);
  • объявить класс ProcessArray с обобщенным статическим методом ProcessArrayReverse();
  • объявить класс DemoRefMethods в котором реализовать шаблонный метод DemoReverse(). В методе DemoReverse() использовать ссылку функциональный интерфейс IArrayFunction для вызова метода Function();
  • в функции main() продемонстрировать передачу статического метода ProcessArrayReverse() в метод DemoReverse().

Текст программы, решающий данную задачу, следующий

// Ссылка на статический метод
// 1. Обобщенный функциональный интерфейс
interface IArrayFunction<T extends Number> {
  // Метод, получающий массив типа T и возвращающий массив типа T
  T[] Function(T[] array);
}

// 2. Класс, который содержит обобщенный статический метод, возвращающий массив чисел
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. Класс, в котором определен метод, получающий ссылку на функциональный интерфейс
class DemoRefMethods {
  // Метод, получающий ссылку на функциональный интерфейс IArrayFunction<T>
  <T extends Number> T[] DemoReverse(IArrayFunction<T> ref, T[] array) {
    return ref.Function(array);
  }
}

// 4. Класс, демонстрирующий использование ссылки на статический метод
public class RefMethod {

  public static void main(String[] args) {
    // Реализовать реверсирование массива целых чисел
    // 1. Объявить экземпляр класса DemoRefMethods
    DemoRefMethods obj = new DemoRefMethods();

    // 2. Тестирующий массив
    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. Передать статический метод  ProcessArray.ProcessArrayReverse() в
    //   метод obj.DemoReverse()
    Integer AI2[] = obj.DemoReverse(ProcessArray::ProcessArrayReverse, AI);

    // 4. Вывести результат - массив AI2
    System.out.println("Array AI2: ");
    for (int i=0; i<AI2.length; i++)
      System.out.print(" " + AI2[i]);
    System.out.println();
  }
}

Результат выполнения программы

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

 

3. Ссылка на методы экземпляра
3.1. Общая форма ссылки на методы экземпляра

Чтобы объявить ссылку на метод экземпляра используется одна из двух общих форм:

имя_экземпляра::имя_метода
имя_класса::имя_метода

В другом случае метод экземпляра указывается вмести з любым объектом данного класса, а не только с указанным объектом.

 

3.2. Примеры использования ссылок на методы экземпляра
3.2.1. Ссылка на методы, выполняющие действия над комплексными числами

Условие задачи. Продемонстрировать передачу в некоторый метод класса методов суммирования и вычитания комплексных чисел.

Решение. Для решения задачи в программе необходимо разработать следующие элементы (составляющие).

1. Класс Complex, определяющий комплексне число

class Complex {
  // Вещественная и мнимая части комплексного числа
  public double re;
  public double im;

  ...
}

2. Функциональный интерфейс для оперирования типом Complex

interface IFunction {
  // Метод, который оперирует комплексными числами типа Complex
  Complex Function(Complex c1, Complex c2);
}

В функциональном интерфейсе объявляется метод Function(). Сигнатура метода определяет получение двух комплексных чисел и возврат комплексного числа. Такая сигнатура может подойти для операций сложения, вычитания, умножения, деления комплексных чисел и других операций.

3. Класс, содержащий собственно методы обработки комплексных чисел (суммирование, вычитание).

class ProcessComplex {
  // Суммирование комплексных чисел
  Complex Add(Complex c1, Complex c2) {
    ...
  }

  // Вычитание комплексных чисел
  Complex Sub(Complex c1, Complex c2) {
    ...
  }
}

4. Класс, содержащий метод, который получает ссылку на метод экземпляра. В нашем случае нужно передавать методы Add() и Sub() экземпляра класса ProcessComplex.

class DemoRefMethods {
  // В этот метод будет передаваться ссылка на методы Add() и Sub()
  // экземпляра класса ProcessComplex
  Complex RefMethod(IFunction ref, Complex c1, Complex c2) {
    ...
  }
}

5. Класс, демонстрирующий использование ссылки на метод экземпляра

// 5. Класс, который демонстрирует использование ссылки на метод экземпляра
public class RefMethod {
  public static void main(String[] args) {
    ...
  }
}

Ниже приведен текст решения задачи.

// Ссылка на метод экземпляра
// 1. Класс, определяющий комплексное число
class Complex {
  // Вещественная и мнимая части комплексного числа
  public double re;
  public double im;

  // Конструктор
  public Complex(double _re, double _im) {
    re = _re;
    im = _im;
  }

  // Метод, который печатает комплексное число
  void Print(String text) {
    System.out.print(text);
    System.out.print(re);
    if (im>0)
      System.out.print("+");
    System.out.println(im);
  }
}

// 2. Функциональный интерфейс, который оперирует типом Complex
interface IFunction {
  // Метод, оперирующий комплексными числами типа Complex
  Complex Function(Complex c1, Complex c2);
}

// 3. Класс, который содержит методы обработки комплексных чисел
class ProcessComplex {
  // Суммирование комплексных чисел
  Complex Add(Complex c1, Complex c2) {
    Complex c3 = new Complex(c1.re+c2.re, c1.im+c2.im);
    return c3;
  }

  // Вычитание комплексных чисел
  Complex Sub(Complex c1, Complex c2) {
    Complex c3 = new Complex(c1.re-c2.re, c1.im-c2.im);
    return c3;
  }
}

// 4. Класс, в котором определен метод, который получает ссылку
//   на функциональный интерфейс IFunction
class DemoRefMethods {
  // Метод, который получает комплексные числа и ссылку
  // на функциональный интерфейс IFunction.
  // В этот метод будет передана ссылка на метод экземпляра
  // класса ProcessComplex
  Complex RefMethod(IFunction ref, Complex c1, Complex c2) {
    Complex c3;
    c3 = ref.Function(c1, c2);
    return c3;
  }
}

// 5. Класс, демонстрирующий использование ссылки на метод экземпляра
public class RefMethod {

  public static void main(String[] args) {
    // Реализовать суммирование и вычитание комплексных чисел
    // 1. Объявить экземпляр класса ProcessComplex
    ProcessComplex obj1 = new ProcessComplex();

    // 2. Объявить экземпляр класса DemoRefMethods
    DemoRefMethods objDemo = new DemoRefMethods();

    // 3. Создать комплексные числа для тестирования
    Complex c1 = new Complex(5, -8);
    Complex c2 = new Complex(3, 4);
    c1.Print("c1 = ");
    c2.Print("c2 = ");

    // 4. Передать в метод objDemo.RefMethod() ссылку на
    //    метод obj1.Add() и вывести результат
    Complex c3 = objDemo.RefMethod(obj1::Add, c1, c2);
    c3.Print("c3 = c1 + c2 = ");

    // 5. Продемонстрировать вычитание комплексных чисел
    Complex c4;
    c4 = objDemo.RefMethod(obj1::Sub, c1, c2); // передать obj1::Sub
    c4.Print("c4 = c1 - c2 = ");
  }
}

Результат выполнения программы

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. Ссылка на обобщенный метод экземпляра

Условие задачи. Реализовать класс, содержащий следующие обобщенные (шаблонные) методы работы с массивами:

  • вычисление суммы элементов массива;
  • вычисление среднего арифметического элементов массива.

Продемонстрировать вызов методов обработки массивов используя способ передачи ссылки на метод экземпляра в качестве параметра.

Решение.

// Ссылка на обобщенный метод экземпляра
// 1. Обобщенный (шаблонный) функциональный интерфейс
interface IFuncArray<T extends Number> {
  // Метод, получающий массив типа T и возвращающий тип double
  double FuncArray(T[] array);
}

// 2. Класс, содержащий методы обработки массивов
class ProcessArray<T extends Number> {
  // Сумма элементов массиву
  double Sum(T[] array) {
    double sum = 0;
    for (T value : array)
      sum += value.doubleValue();
    return sum;
  }

  // Среднее арифметическое элементов массива
  double Avg(T[] array) {
    double avg = 0;
    for (T value : array)
      avg += value.doubleValue();
    return avg/array.length;
  }
}

// 3. Класс, который содержит шаблонный метод,
//   получающий ссылку на метод экземпляра
class ClassOperation {

  <T extends Number> double Operation(IFuncArray<T> ref, T[] array) {
    // вызвать метод интерфейса IFuncArray
    double result = ref.FuncArray(array);
    return result;
  }
}

// 4. Класс, который демонстрирует ссылку на метод экземпляра
public class RefMethod {

  public static void main(String[] args) {
    // 1. Объявить экземпляр класса ClassOperation
    ClassOperation Op = new ClassOperation();

    // 2. Объявить экземпляр класса ProcessArray<T>
    ProcessArray<Integer> Array = new ProcessArray<Integer>();

    // 3. Подготовить массив целых чисел для обработки и вывести его
    Integer[] AI = { 2, 4, 8, -1, 3, -5 };
    System.out.print("AI = ");
    for (int t : AI)
      System.out.print(t + " ");
    System.out.println();

    // 4. Вычислить среднее арифметическое массива AI,
    //    передавая метод Array.Avg() в метод Op.Operation()
    double average = Op.Operation(Array::Avg, AI);
    System.out.println("average = " + average);

    // 5. Вычислить сумму элементов массива AI.
    //     Для этого нужно передать метод Array.Sum() в Op.Operation()
    double sum = Op.Operation(Array::Sum, AI);
    System.out.println("sum = " + sum);
  }
}

Как видно из кода функции main(), передача методов Avg() и Sum() экземпляра Array осуществляется вызовом единственного метода Op.Operation()

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

Разница в вызовах заключается в передаче первого параметра, которым есть ссылка на метод экземпляра. В первом случае передается метод экземпляра, который вычисляет среднее арифметическое по следующему синтаксису

Array::Avg

В другом случае передается метод экземпляра

Array::Sum

который вычисляет сумму.

После запуска на выполнение программа выдаст следующий результат

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

 


Связанные темы