JavaRush /Java блог /Random UA /Сигнатура методу

Сигнатура методу

Стаття з групи Random UA
Вітання! Ти вже освоїв створення власних класів, з полями та методами. Сьогодні ми докладно поговоримо про методи. Ми, звичайно, вже не раз робабо це у наших лекціях, але ми говорабо здебільшого про загальні моменти. Сьогодні ж ми буквально розберемо методи "частинами" - дізнаємося з чого вони складаються, які варіанти їх створення існують і як усім цим можна керувати:) Поїхали!Сигнатура методу - 1

Сигнатура методу

Весь код, який описує метод називається оголошенням методу . Сигнатура методу включає назву методу та типи параметрів у визначеному порядку. Загальний вигляд оголошення можна описати так:
модификатор доступа, тип возвращаемого значения, ім'я метода(список параметров) {
    // тело метода
}
Розглянемо для прикладу оголошень кількох методів класу Dog.
public class Dog {

   String name;

   public Dog(String name) {
       this.name = name;
   }

   public static void main(String[] args) {
       Dog max = new Dog("Макс");
       max.woof();

   }

   public void woof() {
       System.out.println("Собака по имени " + name + " говорит \"Гав-гав!\"");
   }

   public void run(int meters) {
       System.out.println("Собака по имени " + name + " пробежала " + meters + " метров!");
   }

   public String getName() {
       return name;
   }
}

1. Модифікатор доступу

Модифікатор доступу завжди вказується першим. Усі методи класу Dogпозначені модифікатором public. Тобто ми можемо викликати їх з будь-якого іншого класу:
public class Main {

   public static void main(String[] args) {

       Dog butch = new Dog("Бутч");
       butch.run(100);
   }

}
Методи класу Dog, як бачиш, легко доступні у класі Main. Це можливо саме завдяки модифікатору public. У Java є й інші модифікатори, і не всі дозволять використовувати метод всередині іншого класу. Про них ми поговоримо в інших лекціях. Запам'ятай за що відповідає модифікатор: за доступність/недоступність методу в інших класах:)

2. Ключове слово static

Один із методів Dog, а саме main()позначений ключовим словом static. Якщо ж воно є, то вказати його необхідно після модифікатора доступу. Пам'ятаєш, у минулих лекціях ми говорабо про статичні змінні класу? Стосовно методів це слово має приблизно той самий зміст. Якщо метод вказаний як staticце означає, що він може використовуватися без посилання на конкретний об'єкт класу. І щоб запустити статичний метод main()у класі Dogтобі не потрібно створювати екземпляр Dog, він запускається і без цього. Якби цей метод не був статичним, то для його використання нам знадобилося б спершу створити об'єкт.

3. Значення, що повертається.

Якщо наш метод має щось повернути, то далі ми вказуємо тип значення, що повертається. Це видно з прикладу геттера getName():
public String getName() {
   return name;
}
Він повертає об'єкт типу String. Якщо ж метод нічого не повертає - замість типу вказується ключове слово void, як у методі woof():
public void woof() {
   System.out.println("Собака по имени " + name + " говорит \"Гав-гав!\"");
}

Методи з однаковими іменами

Бувають ситуації, як у нашій програмі потрібно кілька варіантів роботи методу. Чому б нам не створити свій власний штучний інтелект? У Amazon є Alexa, у Яндекса — Аліса, так чим ми гірші? Джарвіса - вітатися з людьми, які заходять до кімнати (буде дивно, якщо такий великий інтелект виявиться неввічливим).
public class Jarvis {

   public void sayHi(String name) {
       System.out.println("Добрий вечір, " + name + ", як ваші справи?");
   }

   public static void main(String[] args) {
       Jarvis jarvis = new Jarvis();
       jarvis.sayHi("Тоні Старк");
   }
}
Виведення в консоль:

Добрый вечер, Тони Старк, як ваши дела?
Чудово! Джарвіс вміє вітати того, хто увійшов. Найчастіше, звісно, ​​це буде його господар — Тоні Старк. Але він може прийти не один! А наш метод sayHi()приймає на вхід лише один аргумент. І, відповідно, зможе привітати лише одного з тих, хто прийшов, а іншого проігнорує. Не дуже ввічливо, згоден?
public class Jarvis {

   public void sayHi(String firstGuest) {
       System.out.println("Добрий вечір, " + firstGuest + ", як ваші справи?");
   }

   public void sayHi(String firstGuest, String secondGuest) {
       System.out.println("Добрий вечір, " + firstGuest + ", " + secondGuest + ", як ваші справи?");
   }

}
Це називається перевантаженням методів . Перевантаження дозволяє нашій програмі бути більш гнучкою та враховувати різні варіанти роботи. Перевіримо як це працює:
public class Jarvis {

   public void sayHi(String firstGuest) {
       System.out.println("Добрий вечір, " + firstGuest + ", як ваші справи?");
   }

   public void sayHi(String firstGuest, String secondGuest) {
       System.out.println("Добрий вечір, " + firstGuest + ", " + secondGuest + ", як ваші справи?");
   }

   public static void main(String[] args) {
       Jarvis jarvis = new Jarvis();
       jarvis.sayHi("Тоні Старк");
       jarvis.sayHi("Тоні Старк", "Капітан Америка");
   }
}
Виведення в консоль:

Добрый вечер, Тони Старк, як ваши дела? 
Добрый вечер, Тони Старк, Капитан Америка, як ваши дела?
Відмінно, обидва варіанти спрацювали :) Проте проблему ми не вирішабо! Що, якщо гостей буде троє? Звичайно, ми можемо ще раз перевантажити метод sayHi(), щоб він приймав імена трьох гостей. Але ж їх може бути і 4, і 5. І так до нескінченності. Чи немає іншого способу навчити Джарвіса працювати з будь-якою кількістю імен, без мільйона навантажень методу sayHi()? :/ Звичайно є! Інакше була б хіба Java найпопулярнішою у світі мовою програмування? ;)
public class Jarvis {

   public void sayHi(String...names) {

       for (String name: names) {
           System.out.println("Добрий вечір, " + name + ", як ваші справи?");
       }
   }

   public static void main(String[] args) {
       Jarvis jarvis = new Jarvis();
       jarvis.sayHi("Тоні Старк");
       System.out.println();
       jarvis.sayHi("Тоні Старк", "Капітан Америка");
   }
}
Запис ( String...names) переданий як параметр дозволяє нам вказати, що метод передається якесь кількість рядків. Ми не застерігаємо заздалегідь скільки їх має бути, тому робота нашого методу стає тепер набагато гнучкішою:
public class Jarvis {

   public void sayHi(String...names) {

       for (String name: names) {
           System.out.println("Добрий вечір, " + name + ", як ваші справи?");
       }
   }

   public static void main(String[] args) {
       Jarvis jarvis = new Jarvis();
       jarvis.sayHi("Тоні Старк", "Капітан Америка", "Чорна вдова", "Халк");
   }
}
Виведення в консоль:

Добрый вечер, Тони Старк, як ваши дела? 
Добрый вечер, Капитан Америка, як ваши дела? 
Добрый вечер, Черная Вдова, як ваши дела? 
Добрый вечер, Халк, як ваши дела?
Усередині методу ми у циклі перебираємо всі аргументи та виводимо готові фрази з іменами на консоль. Тут ми використовуємо спрощений цикл for-each(ти вже з ним стикався). Він чудово підходить, тому що запис String...names- насправді означає, що всі передані параметри поміщаються компілятором у масив. Тому зі змінною namesможна працювати як із масивом, у тому числі — перебирати у циклі. При цьому він спрацює за будь-якої кількості переданих рядків! Дві, десять, хоч тисяча – метод стабільно працюватиме з будь-якою кількістю гостей. Набагато зручніше, ніж робити навантаження для всіх можливих варіантів, згоден? Наведемо ще один приклад навантаження методу. Додамо Джарвіс методprintInfoFromDatabase(). Він друкуватиме в консоль інформацію про людину з бази даних. Якщо в базі даних зазначено, що людина є супергероєм або суперлиходієм, ця інформація також буде виведена на екран:
public class Jarvis {

   public  void printInfoFromDatabase (String bio) {

       System.out.println(bio);
   }

   public void printInfoFromDatabase(String bio, boolean isEvil, String nickname) {

       System.out.println(bio);
       if (!isEvil) {
           System.out.println("Также известен як супергерой " + nickname);
       } else {
           System.out.println("Также известен як суперзлодей " + nickname);
       }
   }

   public static void main(String[] args) {
       Jarvis jarvis = new Jarvis();
       jarvis.printInfoFromDatabase("Лора Палмер. Дата рождения - 22 июля 1972, город Твин Пикс, штат Вашингтон");
       System.out.println();
       jarvis.printInfoFromDatabase("Макс Эйзенхарт. Рост 188см, вес 86 кг.", true, "Магнето");
   }
}
Висновок:

Лора Палмер. Дата рождения - 22 июля 1972, город Твин Пикс, штат Вашингтон
Макс Эйзенхарт. Рост 188см, вес 86 кг 
Также известен як суперзлодей Магнето
Ось так, наш метод працює в залежності від даних, які ми в нього передаємо. Ще один важливий момент:порядок дотримання аргументів має значення! Припустимо, наш метод приймає на вхід рядок та число:
public class Man {

   public static void sayYourAge(String greeting, int age) {
       System.out.println(greeting + " " + age);
   }

   public static void main(String[] args) {

       sayYourAge("Мій вік - ", 33);
       sayYourAge(33, "Мій вік - "); //помилка!
   }
}
Якщо метод sayYourAge()класу Manприймає на вхід рядок і число, значить саме в такому порядку їх потрібно передавати в програмі! Якщо ми передамо їх в іншому порядку, компілятор видасть помилку і людина не зможе назвати свій вік. До речі, конструктори, які проходабо в минулій лекції, теж є методами! Їх теж можна перевантажувати (створювати кілька конструкторів з різним набором аргументів) і їм теж важливо важливий порядок передачі аргументів. Справжні методи!:)

Як викликати методи зі схожими параметрами

Як ти знаєш, Java має таке слово як null. Працюючи з ним дуже важливо розуміти, що null перестав бути ні об'єктом, ні типом даних. Уяви собі, що у нас є клас Man і метод introduce(), який оголошує ім'я людини та її вік. При цьому вік можна передати у формі тексту, а можна – числом.
public class Man {

   public void introduce(String name, String age) {
       System.out.println("Меня зовут " + name + ", мой возраст - " + age);
   }

   public void introduce(String name, Integer age) {
       System.out.println("Меня зовут " + name + ", мой возраст - " + age);
   }

   public static void main(String[] args) {

       Man sasha = new Man();
       sasha.introduce("Саша", "двадцать один");

       Man masha = new Man();
       masha.introduce("Мария", 32);
   }
}
З перевантаженням ми вже знайомі, тому знаємо, що метод обидва рази відпрацює як слід:

Меня зовут Саша, мой возраст - двадцать один 
Меня зовут Мария, мой возраст - 32 
Але що буде, якщо як другий параметр ми передаємо не рядок і не число, а null?
public static void main(String[] args) {

   Man victor = new Man();
   victor.introduce("Виктор", null);//Ambiguous method call!
}
Ми матимемо помилку компіляції! Помилка "Ambiguous method call" перекладається як "двозначний виклик методу". Через що вона могла виникнути і в чому полягає "двозначність"? Насправді, все просто. Справа в тому, у нас є два варіанти способу: з Stringі з Integerяк другий аргумент. Але і String, і Integerможе бути null! Для обох типів (оскільки вони посилання) null є значенням за промовчанням. Саме тому компілятор у цій ситуації неспроможна розібратися, який варіант методу має викликати. Вирішити цю проблему досить просто. Справа в тому, що null можна явно перетворити до конкретного типу посилань. Тому при виклику методу ти можеш вказати в дужках потрібний тип даних для другого аргументу! Компілятор зрозуміє твій “натяк” і викличе потрібний метод:
public class Man {

   public void introduce(String name, String age) {
       System.out.println("Метод с двумя строками!");
       System.out.println("Меня зовут " + name + ", мой возраст - " + age);
   }

   public void introduce(String name, Integer age) {
       System.out.println("Метод со строкой и числом!");
       System.out.println("Меня зовут " + name + ", мой возраст - " + age);
   }

   public static void main(String[] args) {

       Man victor = new Man();
       victor.introduce("Виктор", (String) null);
   }
}
Висновок:

Метод с двумя строками! 
Меня зовут Виктор, мой возраст - null
А от якби числовий параметр був примітивом int, а не об'єктом типу посилань Integer— такої помилки не виникло б.
public class Man {

   public void introduce(String name, String age) {
       System.out.println("Метод с двумя строками!");
       System.out.println("Меня зовут " + name + ", мой возраст - " + age);
   }

   public void introduce(String name, int age) {
       System.out.println("Метод со строкой и числом!!");
       System.out.println("Меня зовут " + name + ", мой возраст - " + age);
   }

   public static void main(String[] args) {

       Man victor = new Man();
       victor.introduce("Виктор", null);
   }
}
Здогадався чому? Якщо здогадався — молодець:) Тому що примітиви не можуть дорівнювати null. Тепер компілятор залишив лише один варіант виклику методу introduce()— з двома рядками. Саме цей варіант методу і відпрацьовуватиме щоразу при виклику методу.
Коментарі
ЩОБ ПОДИВИТИСЯ ВСІ КОМЕНТАРІ АБО ЗАЛИШИТИ КОМЕНТАР,
ПЕРЕЙДІТЬ В ПОВНУ ВЕРСІЮ