Методы Java
Объявляйте и вызывайте методы в Java — типы возврата, параметры и тело метода — для организации повторно используемого кода.
Метод в Java — это именованный блок кода, который можно вызвать из других частей программы. Методы помогают избежать дублирования: вместо того чтобы писать одно и то же вычисление или одну и ту же пятистрочную операцию в трёх местах, вы помещаете их в метод и вызываете три раза.
Вы уже видели один метод на каждой странице этой книги — main. Каждая программа на Java — это просто класс хотя бы с одним методом, который JVM умеет запускать. В этой части книги мы рассмотрим, как писать собственные методы.
Анатомия метода
Объявление метода состоит из пяти частей: модификаторы, тип возврата, имя, список параметров и тело.
public static int square(int n) {
return n * n;
}public static— модификаторы.publicуправляет видимостью;staticозначает, что метод принадлежит классу, а не экземпляру. Мы подробно рассмотрим оба модификатора в последующих частях; пока что каждый метод, который вы пишете в верхнеуровневом утилитарном классе, будетpublic static.int— тип возврата. Тип значения, которое метод возвращает вызывающему коду. Используйтеvoid, если метод ничего не возвращает.square— имя. По соглашению используется lowerCamelCase, имя должно быть глаголом или глагольным словосочетанием, описывающим, что делает метод.(int n)— список параметров. Входные данные, которые должен передать вызывающий код. Каждый параметр — это тип и имя.{ ... }— тело. Код, который выполняется при вызове метода.
Вызов метода
После определения метод вызывается по его имени, за которым следует (...) с передаваемыми аргументами:
int result = square(7); // result is 49
System.out.println(square(3) + square(4)); // 9 + 16 = 25Значение, возвращаемое методом, можно использовать как любое другое выражение — присваивать переменной, передавать в качестве аргумента, выводить на экран, складывать с чем-либо.
Оператор return
return делает две вещи: немедленно останавливает выполнение метода и передаёт значение обратно вызывающему коду.
public static int absolute(int n) {
if (n < 0) {
return -n;
}
return n;
}Как только выполняется return -n, метод завершается — второй return n не выполняется. Метод с типом возврата, отличным от void, должен возвращать значение на каждом возможном пути выполнения кода; компилятор откажется компилировать программу, если какой-либо путь пропущен.
Для методов void можно использовать голый return; для досрочного выхода, но писать его в конце необязательно:
public static void greet(String name) {
if (name == null) return; // early exit
System.out.println("Hello, " + name);
} // implicit return at the closing braceПараметры и аргументы
Имена в сигнатуре метода — это параметры; значения, передаваемые в точке вызова, — это аргументы. Различие невелико, но его стоит понять правильно:
public static int sum(int a, int b) { // a, b are parameters
return a + b;
}
int x = 3, y = 4;
int total = sum(x, y); // x, y are argumentsКаждый вызов получает собственные свежие копии параметров. Присваивание значения a внутри sum не изменяет x в точке вызова. Глава о передаче по значению подробно рассматривает эту тему.
Правила типов возврата
Выражение после return должно быть совместимо по присваиванию с объявленным типом возврата — быть того же типа или типа, который Java может неявно расширить до него:
public static double half(int n) {
return n / 2.0; // int / double → double, fine
}
public static int half(int n) {
return n / 2.0; // ERROR: double cannot be implicitly narrowed to int
}Если действительно нужно сузить тип, используйте явное приведение: return (int)(n / 2.0);.
Методы могут вызывать другие методы
Тело метода — это обычный код, в том числе вызовы других методов и даже самого себя (см. рекурсию):
public static int square(int n) { return n * n; }
public static int sumOfSquares(int a, int b) {
return square(a) + square(b);
}Создание небольших, единственной цели методов и их компоновка — это суть написания читаемого кода на Java.
Возврат значения — это не вывод на экран
Распространённая ошибка новичков: метод, который выводит значение, и метод, который возвращает его, не взаимозаменяемы. return передаёт значение обратно, чтобы вызывающий код мог его использовать; System.out.println только записывает текст в консоль, и вызывающий код ничего не получает.
public static void printSquare(int n) {
System.out.println(n * n); // shows the result, returns void
}
public static int square(int n) {
return n * n; // gives the result back to the caller
}Результат square можно сохранить, добавить к чему-либо или передать дальше, но int x = printSquare(5); не скомпилируется — printSquare возвращает void. Предпочтительнее возвращать значение и позволять вызывающему коду решать, выводить его или нет; это делает метод повторно используемым.
Перегрузка: одно имя, разные параметры
Java позволяет двум и более методам иметь одно и то же имя при условии, что их списки параметров различаются — по количеству параметров или их типам. Это называется перегрузкой, и компилятор выбирает нужный метод на основании переданных аргументов:
public static int max(int a, int b) { return a > b ? a : b; }
public static double max(double a, double b) { return a > b ? a : b; }
public static int max(int a, int b, int c) { return max(max(a, b), c); }max(3, 9) вызывает первый метод, max(2.5, 1.5) — второй, а max(4, 1, 7) — третий. Только тип возврата недостаточен для различения перегрузок — имеют значение только параметры. Глава о перегрузке методов подробно рассматривает правила сопоставления.
Где живут методы
Метод всегда находится внутри класса. Нельзя написать функцию верхнего уровня, как это возможно в JavaScript или Python. Класс — это контейнер; метод — один из его членов. Таким образом, полный файл с двумя методами выглядит следующим образом:
public class MathUtils {
public static int square(int n) {
return n * n;
}
public static int cube(int n) {
return n * n * n;
}
}Из main того же класса их вызывают как square(5) и cube(2). Из другого класса нужно уточнять: MathUtils.square(5).
Практический пример
Что дальше
Теперь, когда вы умеете объявлять и вызывать методы, следующий вопрос — как данные передаются в них. Глава о параметрах подробно рассматривает детали — как аргументы сопоставляются с параметрами, какие типы допустимы и что происходит, когда вызывающий код передаёт объекты вместо примитивов.