Метод в Java — это функция, которая определяет, что умеет делать объект этого класса. Одна из главных задач методов — выполнение действий над данными объекта. Они могут менять значение, преобразовывать данные, выводить их в консоль.
Методы можно перегружать и переопределять. Как это делать и в чём разница между этими двумя механизмами — разберёмся в этой статье.
Перегрузка методов в Java — это использование одного имени метода с разными параметрами.
Чтобы разобраться с этим механизмом, начнём с простого — создадим помощника, который будет здороваться с пользователями.
public class Assistant {
public void sayHello(String name) {
System.out.println("Добрый день, " + name + "!");
}
public static void main(String[] args) {
Assistant assistant = new Assistant();
assistant.sayHello("Михаил");
}
}
В консоли будет выведена фраза «Добрый день, Михаил!».
Допустим, Михаил пришёл не один, а с другом Виталием. Сейчас метод реализован так, что помощник поприветствует только Михаила, а Виталия проигнорирует. Чтобы исправить это, реализуем в классе два метода. Имя у них будет одинаковое. Но параметры они принимают разные.
public class Assistant {
public void sayHello(String firstGuest) {
System.out.println("Добрый вечер, " + firstGuest + "!");
}
public void sayHello(String firstGuest, String secondGuest) {
System.out.println("Добрый день, " + firstGuest + " и " + secondGuest + "!");
}
public static void main(String[] args) {
Assistant assistant = new Assistant();
assistant.sayHello("Михаил", "Виталий");
}
}
Теперь в консоли отобразится фраза «Добрый день, Михаил и Виталий!».
Мы уже перегрузили sayHello(). Теперь программа стала более гибкой — помощник может приветствовать сразу двух гостей. Но что произойдёт, если придут трое, четверо или пятеро? Проверим:
public class Assistant {
public void sayHello(String firstGuest) {
System.out.println("Добрый вечер, " + firstGuest + "!");
}
public void sayHello(String firstGuest, String secondGuest) {
System.out.println("Добрый день, " + firstGuest + " и " + secondGuest + "!");
}
public static void main(String[] args) {
Assistant assistant = new Assistant();
assistant.sayHello("Михаил", "Виталий", "Марина");
}
}
В ответ получим ошибку, потому что sayHello() готов принимать только два аргумента. Решение в лоб — перегружать его дальше. Сделать так, чтобы .sayHello() принимал троих, четверых, пятерых и больше гостей. Но это не похоже на гибкую работу программы. Придётся постоянно дописывать код.
Более гибкое решение — передать в качестве параметра аргумент переменной длины (String… names). Это позволит sayHello() принимать любое количество строк. А чтобы выводить в консоль приветствие каждого гостя, используем цикл.
public class Assistant {
public void sayHello(String firstGuest) {
System.out.println("Добрый вечер, " + firstGuest + "!");
}
public void sayHello(String… names) {
for (String name: names) {
System.out.println("Добрый вечер, " + name + "!");
}
}
public static void main(String[] args) {
Assistant assistant = new Assistant();
assistant.sayHello("Михаил", "Виталий", "Марина", "Андрей", "Анна");
}
В консоли отобразится приветствие каждого переданного гостя:
Добрый вечер, Михаил!
Добрый вечер, Виталий!
Добрый вечер, Марина!
Добрый вечер, Андрей!
Добрый вечер, Анна!
В примере выше мы не думали о порядке аргументов, потому что все они были строками. Нет разницы, с кем здороваться сначала — с Михаилом или с Анной.
Но порядок аргументов имеет значение, если метод принимает, например, строку и число. Посмотрите:
public class User {
public static void sayYourAge(String greeting, int age) {
System.out.println(greeting + " " + age);
}
public static void main(String[] args) {
sayYourAge(20, "Мой возраст - "); //ошибка!
}
}
На этапе компиляции возникнет ошибка, потому что при определении sayYourAge() мы задали, что сначала должна быть строка, а затем — число, но аргументы передали в обратном порядке.
Чтобы исправить ошибку, достаточно передать аргументы в правильном порядке:
public class User {
public static void sayYourAge(String greeting, int age) {
System.out.println(greeting + " " + age);
}
public static void main(String[] args) {
sayYourAge("Мой возраст - ", 20);
}
}
Чтобы избежать ошибок, можно сделать перегрузку порядком параметров. Например, вот так:
public class User {
public static void sayYourAge(String greeting, int age) {
System.out.println(greeting + " " + age);
}
public static void sayYourAge(int age, String greeting) {
System.out.println(greeting + " " + age);
}
public static void main(String[] args) {
sayYourAge("Мой возраст - ", 20);
sayYourAge(20, "Мой возраст - ");
}
}
Теперь не имеет значения, в каком порядке передавать аргументы — оба варианты будут понятны программе.
cloud
Из примеров выше можно выделить три варианта перегрузки.
public class Calculator {
void calculate(int number1, int number2) { }
void calculate(int number1, int number2, int number3) { }
}
public class Calculator {
void calculate(int number1, int number2) { }
void calculate(double number1, double number2) { }
}
public class Calculator {
void calculate(double number1, int number2) { }
void calculate(int number1, double number2) { }
}
Напоследок повторим, что означает перегрузка метода в Java. Это механизм языка, который позволяет создавать несколько методов с одинаковым названием, но разными параметрами. Так можно делать не во всех языках.
Перегрузка — это часть полиморфизма, одной из ключевых составляющих объектно-ориентированного программирования. Главный плюс перегрузки в Java — можно использовать схожие методы с одинаковыми именами.
Переопределение метода в Java позволяет взять метод родительского класса и создать специфическую реализацию в классе-наследнике.
Проще понять на примере. Допустим, вы создаёте класс Animal с методом voice(). Он нужен для того, чтобы животное могло подать голос:
public class Animal {
public void voice() {
System.out.println("Говори!");
}
}
И сразу возникает проблема — все животные издают разные звуки. Можно создать для каждого отдельный метод. Например, у кошки это будет voiceCat(), а у собаки — voiceDog(). Но представьте, сколько строк кода придётся написать, чтобы дать возможность всем животным подать голос?
Здесь на помощь и приходит механизм переопределения в Java. Он позволяет заменить реализацию в классе-наследнике. Посмотрим на примере кошки и собаки:
public class Cat extends Animal {
@Override
public void voice() {
System.out.println("Мяу!");
}
}
public class Dog extends Animal {
@Override
public void voice() {
System.out.println("Гав!");
}
}
В выводе отобразится сначала «Мяу», а затем — «Гав». Чтобы добиться такого результата, нужно:
Собственная реализация пишется для каждого класса-наследника. Если этого не сделать, то будет использована реализация родительского класса.
Даже после переопределения вы можете обратиться к методу родительского класса при условии, что он не определён модификатором private. Для этого используется ключевое слово super:
super.method();
У переопределения методов класса в Java есть ряд ограничений.
Например:
class Parent {
final void show() {}
}
class Child extends Parent {
void show() {}
}
Такой код вернёт ошибку, потому что в родительском классе использовано ключевое слово final.
Переопределение класса в Java подчиняется указанным выше правилам, которые необходимо соблюдать.
Собственные правила переопределения есть у отдельных методов. Например, equals() и hashCode(). Самое важное условие — если вы переопределяете equals(), то должны переопределить и hashCode(). В противном случае классы и методы, которые пользуются контрактами стандартной реализации этих двух методов, могут работать с ошибками. Подробнее от этом мы рассказали в отдельной статье.
Выгодные тарифы на облако в Timeweb Cloud
Переопределение и перегрузка методов в Java — важные части полиморфизма, однако это разные механизмы. При перегрузке вы создаёте внутри одного класса много методов с одинаковым названием, но разными параметрами. При переопределении вы берёте один и тот же метод и заставляете его делать разные вещи в зависимости от того, в каком классе его вызвали.
Но есть характеристики, в которых перегрузка и переопределение Java похожи. Оба механизма помогают сделать код чище и читабельнее, а также уменьшить количество ошибок при выполнении программ.