Перегрузка методов в программировании

Перегрузка и переопределение методов в Java: примеры

Метод в 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, "Мой возраст - ");
>
>

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

Варианты перегрузки

Из примеров выше можно выделить три варианта перегрузки.

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("Гав!");
>
>

В выводе отобразится сначала «Мяу», а затем — «Гав». Чтобы добиться такого результата, нужно:

  1. В классе-наследнике создать метод с таким же именем, как в родительском классе.
  2. Добавить перед ним аннотацию @Override (с английского переводится как «переопределён»). Эта аннотация сообщит компилятору, что это не ошибка, вы намеренно переопределяете метод. Отметим, что наличие аннотации необязательно. Если в дочернем классе создать метод с такой же сигнатурой, как у родительского, метод все равно переопределится. Но рекомендуется ставить аннотацию всегда, так как это улучшает «читабельность» кода, а также при этом компилятор проверит на этапе сборки, что такой метод действительно есть в родительском классе.

Собственная реализация пишется для каждого класса-наследника. Если этого не сделать, то будет использована реализация родительского класса.

Даже после переопределения вы можете обратиться к методу родительского класса при условии, что он не определён модификатором private. Для этого используется ключевое слово super:

Ограничения при переопределении

У переопределения методов класса в Java есть ряд ограничений.

  • Название метода должно быть таким же, как у метода родителя (то есть сигнатура метода должна быть одинаковой).
  • Аргументы должны оставаться такими же, как у метода родителя.
  • Тип возвращаемого значения должен быть таким же, как у метода родителя.
  • Модификатор доступа должен быть таким же, как у метода родителя.
  • Окончательные методы (final) нельзя переопределять. Это один из способов запрета переопределения — объявить метод с помощью ключевого слова final.
class Parent final void show() <>
>

class Child extends Parent void show() <>
>

Такой код вернёт ошибку, потому что в родительском классе использовано ключевое слово final.

  • Статические методы (static) нельзя переопределять. Если вы определите в классе-наследнике такую же сигнатуру метода, как в родительском классе, то выполните сокрытие. Подробнее об этом вы можете прочитать в документации .
  • Приватные методы (private) нельзя переопределять, так как они связываются на этапе компиляции, а не выполнения.
  • Нельзя сужать модификатор доступа — например, с public до private. Расширение уровня доступа возможно.
  • Нельзя менять тип возвращаемого значения, однако можно сузить возвращаемое значение, если они совместимы.

Переопределение класса в Java подчиняется указанным выше правилам, которые необходимо соблюдать.

Собственные правила переопределения есть у отдельных методов. Например, equals() и hashCode(). Самое важное условие — если вы переопределяете equals(), то должны переопределить и hashCode(). В противном случае классы и методы, которые пользуются контрактами стандартной реализации этих двух методов, могут работать с ошибками. Подробнее от этом мы рассказали в отдельной статье.

Заключение

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

Но есть характеристики, в которых перегрузка и переопределение Java похожи. Оба механизма помогают сделать код чище и читабельнее, а также уменьшить количество ошибок при выполнении программ.

Источник

Перегрузка методов в программировании

Иногда возникает необходимость создать один и тот же метод, но с разным набором параметров. И в зависимости от имеющихся параметров применять определенную версию метода. Такая возможность еще называется перегрузкой методов (method overloading).

И в языке C# мы можем создавать в классе несколько методов с одним и тем же именем, но разной сигнатурой. Что такое сигнатура? Сигнатура складывается из следующих аспектов:

  • Имя метода
  • Количество параметров
  • Типы параметров
  • Порядок параметров
  • Модификаторы параметров

Но названия параметров в сигнатуру НЕ входят. Например, возьмем следующий метод:

public int Sum(int x, int y)

У данного метода сигнатура будет выглядеть так: Sum(int, int)

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

  • Количеству параметров
  • Типу параметров
  • Порядку параметров
  • Модификаторам параметров

Например, пусть у нас есть следующий класс:

class Calculator < public void Add(int a, int b) < int result = a + b; Console.WriteLine($"Result is "); > public void Add(int a, int b, int c) < int result = a + b + c; Console.WriteLine($"Result is "); > public int Add(int a, int b, int c, int d) < int result = a + b + c + d; Console.WriteLine($"Result is "); return result; > public void Add(double a, double b) < double result = a + b; Console.WriteLine($"Result is "); > >

Здесь представлены четыре разных версии метода Add, то есть определены четыре перегрузки данного метода.

Первые три версии метода отличаются по количеству параметров. Четвертая версия совпадает с первой по количеству параметров, но отличается по их типу. При этом достаточно, чтобы хотя бы один параметр отличался по типу. Поэтому это тоже допустимая перегрузка метода Add.

То есть мы можем представить сигнатуры данных методов следующим образом:

Add(int, int) Add(int, int, int) Add(int, int, int, int) Add(double, double)

После определения перегруженных версий мы можем использовать их в программе:

Calculator calc = new Calculator(); calc.Add(1, 2); // 3 calc.Add(1, 2, 3); // 6 calc.Add(1, 2, 3, 4); // 10 calc.Add(1.4, 2.5); // 3.9
Result is 3 Result is 6 Result is 10 Result is 3.9

Также перегружаемые методы могут отличаться по используемым модификаторам. Например:

void Increment(ref int val) < val++; Console.WriteLine(val); >void Increment(int val)

В данном случае обе версии метода Increment имеют одинаковый набор параметров одинакового типа, однако в первом случае параметр имеет модификатор ref. Поэтому обе версии метода будут корректными перегрузками метода Increment.

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

int Sum(int x, int y) < return x + y; >int Sum(int number1, int number2) < return number1 + number2; >void Sum(int x, int y)

Сигнатура у всех этих методов будет совпадать:

Поэтому данный набор методов не представляет корректные перегрузки метода Sum и работать не будет .

Источник

Читайте также:  Программирование html все тэги
Оцените статью