W3docs

Параметры методов Java

Передавайте значения в методы Java через параметры и узнайте разницу между параметрами и аргументами.

Параметры — это входные данные, которые объявляет метод; аргументы — это значения, которые вы передаёте при вызове. Компилятор проверяет, что аргументы соответствуют параметрам по типу и позиции, а затем передаёт их методу как локальные переменные, доступные в теле метода.

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

Объявление параметров

Список параметров метода — это последовательность пар Тип имя, разделённых запятыми:

public static void greet(String name, int times) {
  for (int i = 0; i < times; i++) {
    System.out.println("Hello, " + name);
  }
}

Метод может объявлять ноль, один или несколько параметров. Каждый из них требует собственного типа — нет сокращения вроде int a, b (это int a и b, что не скомпилируется).

Вызов с аргументами

В месте вызова значения передаются в том же порядке, в котором объявлены параметры:

greet("Ada", 3);    // name = "Ada", times = 3

Компилятор проверяет два условия:

  1. Арность — количество аргументов совпадает с количеством параметров.
  2. Типы — тип каждого аргумента совместим с типом соответствующего параметра.

Если одна из проверок не пройдена, программа не скомпилируется. Если передать String там, где ожидается int, вы получите ошибку вида incompatible types: String cannot be converted to int.

Параметры как локальные переменные

Внутри метода каждый параметр ведёт себя как локальная переменная, инициализированная значением аргумента:

public static int doubleIt(int n) {
  n = n * 2;          // legal: n is just a local
  return n;
}

int x = 5;
int result = doubleIt(x);   // result is 10
System.out.println(x);       // still 5

Переприсваивание n внутри doubleIt не влияет на x в месте вызова. Каждый вызов получает свою собственную копию параметра. Именно это имеют в виду, когда говорят, что Java использует передачу «по значению» — каждый аргумент копируется в слот параметра. В следующих главах рассматриваются последствия этого, особенно для аргументов-объектов.

Информация
Java всегда использует передачу по значению, даже для объектов. При передаче ссылочного типа копируется сама ссылка, а не объект — поэтому метод может изменять объект, на который указывает ссылка, но переприсваивание параметра по-прежнему не меняет переменную в вызывающем коде. Глава передача по значению разбирает это подробно.

Неявное расширение типов

Java автоматически расширяет числовой тип меньшего размера до большего, чтобы соответствовать параметру:

public static double half(double x) {
  return x / 2;
}

int n = 7;
double result = half(n);   // n is widened from int to double

Расширение допустимо, потому что при этом не теряется информация. Сужение не происходит неявно — чтобы передать double в параметр типа int, требуется явное приведение:

half(7.5);            // fine: double argument, double parameter
double d = 3.7;
int i = (int) d;      // explicit cast
half(i);              // also fine

Те же правила расширения применяются, что и при присваивании: byte → short → int → long → float → double и char → int.

Параметры-объекты

Можно передавать любой объект — String, массив, пользовательский класс — объявив параметр соответствующего ссылочного типа:

public static int firstLength(String[] words) {
  if (words.length == 0) return 0;
  return words[0].length();
}

String[] colors = {"red", "green", "blue"};
System.out.println(firstLength(colors));   // 3

Аргумент также может быть null, и в этом случае параметр будет null, а любое обращение к полю или вызов метода на нём бросит NullPointerException. Методы, принимающие ссылки, обычно выполняют проверку заранее или документируют, что null недопустим.

Передача null

null совместим по присваиванию с любым ссылочным типом, поэтому его можно передавать в любой параметр-объект:

greet(null, 1);

Если метод обращается к параметру (name.length(), name.charAt(0), …), это приводит к исключению. Защитный метод явно обрабатывает null, часто с помощью проверки в начале:

public static void greet(String name, int times) {
  if (name == null) name = "stranger";
  for (int i = 0; i < times; i++) {
    System.out.println("Hello, " + name);
  }
}

Параметры с модификатором final

Параметр можно пометить как final, чтобы запретить его переприсваивание внутри метода:

public static int doubleIt(final int n) {
  // n = n * 2;     // ERROR: cannot assign a value to final variable n
  return n * 2;
}

Это не меняет того, что видит вызывающий код — только то, разрешено ли телу метода повторно использовать слот параметра. Многие руководства по стилю рекомендуют считать все параметры final даже без ключевого слова: просто вычисляйте новые значения, не изменяя параметр.

Порядок вычисления аргументов

Когда вы пишете f(a(), b(), c()), аргументы вычисляются слева направо — все до вызова f. Если a() бросает исключение, b() и c() не выполняются.

public static int log(String label, int value) {
  System.out.println(label + " = " + value);
  return value;
}

public static int sum(int a, int b, int c) {
  return a + b + c;
}

sum(log("a", 1), log("b", 2), log("c", 3));
// prints:
//   a = 1
//   b = 2
//   c = 3

Этот детерминизм отличает Java от C и C++; в Java порядок фиксирован и предсказуем.

Рабочий пример

java— editable, runs on the server

Что дальше

Теперь вы знаете, как данные попадают в метод. Иногда нужно два метода с одним именем, но разными входными данными — например, один принимает int, а другой String. Java позволяет написать оба сразу с помощью перегрузки методов.

Практика

Практика
Что происходит с примитивной переменной, когда она передаётся как аргумент в метод?
Что происходит с примитивной переменной, когда она передаётся как аргумент в метод?
Was this page helpful?