1. Передача параметров

А теперь начинается самое интересное. Как вы уже, наверное, знаете, по методам типа System.out.println(), в методы можно передавать параметры. Что, собственно говоря, очень сильно повышает пользу от создания и использования методов.

Так как же нам объявить метод с параметрами? На самом деле это довольно просто:

public static void имя(параметры)
{
   код метода
}

Где имя — это уникальное имя метода, а код метода — это команды, из которых состоит метод. А параметры — это параметры метода, перечисленные через запятую. Давайте лучше распишем этот шаблон более детально:

public static void имя(тип1 имя1, тип2 имя2, тип3 имя3)
{
   код метода
}

Примеры:

Код Пояснение
public static void print(String str)
{
}
Объявлен метод print с параметром
String str
public static void print(String str, int count)
{
}
Объявлен метод print с двумя параметрами
String str
int count
public static void write(int x, int y)
{
}
Объявлен метод write с двумя параметрами
int x
int y

Если мы не хотим, чтобы у метода были параметры, просто оставляем круглые скобки пустыми.

Параметры — это специальные переменные метода. С их помощью в метод можно передавать различные значения при его вызове.

Давайте, например, напишем метод, который выводит на экран строку текста заданное количество раз.

Как написать код по выводу строки на экран несколько раз, вы уже знаете. Но какую именно строку выводить? И сколько раз? Вот для этого нам параметры и понадобятся.

Вот как будет выглядеть код этой задачи:

Код Пояснение
class Solution
{
   public static void printLines(String text, int count)
   {
     for (int i = 0; i < count; i++)
       System.out.println(text);
   }

   public static void main(String[] args)
   {
     printLines("Привет", 10);
     printLines("Пока", 20);
   }
}


Объявили метод printLines с параметрами:
String text, int count
Метод выводит на экран count раз строку text





Вызываем метод printLines с разными параметрами

Каждый раз при вызове метода его переменным-параметрам присваиваются переданные значения, и только потом начинают выполняться команды внутри метода


2. Аргументы метода

Еще немного вашего внимания хочу обратить на вызов метода с параметрами.

Те значения, которые передаются в метод, при его вызове принято называть аргументами метода.

Давайте еще раз разберем пример:

Код Пояснение
class Solution
{
   public static void printLines(String text, int count)
   {
     for (int i = 0; i < count; i++)
       System.out.println(text);
   }

   public static void main(String[] args)
   {
     printLines("Привет", 10);
     printLines("Пока", 20);
   }
}


Объявили метод printLines с параметрами:
String text, int count
Метод выводит на экран count раз строку text




Вызываем метод printLines с параметрами
text = "Привет"; count = 10;
text = "Пока"; count = 20;

Когда метод printLines вызвался в первый раз, его переменным-параметрам были присвоены такие значения:
String text = "Привет", int count = 10.

Когда метод printLines вызвался во второй раз, его переменным-параметрам были присвоены другие значения:
String text = "Пока", int count = 20.

Параметры метода — это именно переменные, которым присваиваются определенные значения при вызове метода. Сами же значения "Привет", "Пока", 10 и 20 называются аргументами метода.


3. Конфликт имен переменных при вызове метода

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

Код Пояснение
class Solution
{
   public static void printLines(String text, int count)
   {
     for (int i = 0; i < count; i++)
       System.out.print(text);
   }

   public static void main(String[] args)
   {
     String str = "Привет";
     int n = 10;
     printLines(str, n);
   }
}


Объявили метод printLines с параметрами:
String text, int count
Метод выводит на экран count раз строку text







Вызываем метод printLines с параметрами:
text = str;
count = n;

Пока ничего сложного: у нас есть переменная str, ее значение присваивается переменной text при вызове метода. У нас есть переменная n, ее значение присваивается переменной count при вызове метода. Пока все понятно.

А теперь давайте переименуем наши переменные в методе main:

Код Пояснение
class Solution
{
   public static void printLines(String text, int count)
   {
     for (int i = 0; i < count; i++)
       System.out.print(text);
   }

   public static void main(String[] args)
   {
     String text = "Привет";
     int count = 10;
     printLines(text, count);
   }
}


Объявили метод printLines с параметрами:
String text, int count
Метод выводит на экран count раз строку text







Вызываем метод printLines с параметрами:
text = text;
count = count;

Обратите внимание на две вещи

Первое: у нас есть переменные с одинаковыми именами в разных методах. Это разные переменные (мы их специально раскрасили в разные цвета). Все работает так же, как и в предыдущем примере, когда переменные в методе main назывались str и n.

Второе: при вызове метода никакого волшебства не происходит. Переменным-параметрам просто присваиваются значения аргументов. Независимо от того, это конкретные числа, строки, переменные или выражения.

После переименования переменных в методе main ничего не поменялось. Это как были разные переменные в разных методах, так и остались. Никакой магической связи между переменными text и text нет.



4. Передача ссылок в методы

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

Как вы уже знаете, некоторые переменные в Java хранят не сами значения, а адрес (ссылку) блока памяти, где эти значения находятся. Так, например работают переменные-строки или переменные-массивы.

Когда вы присваиваете переменной-массиву другую переменную-массив, что происходит? Правильно. Две переменные начинают ссылаться на один и тот же контейнер в памяти:

Передача ссылок в методы

А что произойдет, если одна из этих переменных будет переменной-параметром метода?

Код Пояснение
class Solution
{
   public static void printArraySum(int[] data)
   {
     int sum = 0;
     for (int i = 0; i < data.length; i++)
       sum = sum + data[i];
     System.out.println(sum);
   }
   
   public static void main(String[] args)
   {
     int[] months = {31, 28, 31, 30, 31, 30, 31, 31, 30};
     printArraySum(months);
   }
}


Метод printArraySum считает сумму чисел переданного массива и выводит ее на экран

Произойдет ровно то же самое: переменная-параметр data будет содержать ссылку на тот же контейнер, что и переменная months. При вызове метода просто произойдет присваивание data = months.

А раз обе переменные ссылаются на один и тот же контейнер целых чисел, то метод printArraySum может не просто читать значения из массива, но и менять их!

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

Код Пояснение
class Solution
{
   public static void fill(int[][] data, int value)
   {
     for (int i = 0; i < data.length; i++)
     {
       for (int j = 0; j < data[i].length; j++)
         data[i][j] = value;
     }
  }

   public static void main(String[] args)
   {
     int[][] months = {{31, 28}, {31, 30, 31}, {30, 31, 31}};
     fill (months, 8);
   }
}


Метод fill проходится по всем ячейкам переданного двумерного массива и присваивает им значение value.








Создаем двумерный массив.
Заполняем весь массив числом 8.


5. Методы с одинаковыми именами

А теперь еще раз вернемся к именам методов.

Стандарты Java требуют, чтобы у всех методов внутри одного класса были уникальные имена. Т.е. невозможно объявить в одном классе два одинаковых метода.

Только вот при сравнении методов на одинаковость учитываются не только имена, но и типы параметров! Причем имена переменных-параметров не учитываются. Примеры:

Код Пояснение
void fill(int[] data, int value) {
}
void fill(int[][] data, int value) {
}
void fill(int[][][] data, int value)  {
}
Эти три метода считаются разными. Их можно объявить в одном классе.
void print(String str) {
}
void print(String str, String str2) {
}
void print(int val) {
}
void print(double val) {
}
void print() {
}
Все эти пять методов считаются разными. Их можно объявить в одном классе.
void sum(int x, int y) {
}
void sum(int data, int value) {
}
Эти два метода считаются одинаковыми (их нельзя объявить в одном классе).

Почему же одни методы считаются одинаковыми, а другие — разными? И почему не учитываются имена переменных-параметров при определении уникальности метода?

Зачем вообще нужна уникальность? Все дело в том, что когда компилятор компилирует вашу программу, он должен точно знать, какую именно функцию/метод вы вызываете в определенном месте.

Например вы пишете System.out.println("Привет") Компилятор умный, и он легко сделает вывод, что тут вызывается метод println() с параметром типа String.

А если вы напишете System.out.println(1.0), компилятор увидит тут вызов метода println() с параметром типа double.

Компилятор следит, чтобы при вызове метода типы аргументов и параметров совпадали, а на имя аргумента не обращает никакого внимания. В Java имена переменных-параметров никак не помогают компилятору определить вызываемый метод. Поэтому и не учитываются при определении уникальности метода.

Имя метода и типы его параметров называются сигнатурой метода. Пример: sum(int, int)

Каждый класс должен иметь не методы с уникальными именами, а методы с уникальными сигнатурами.