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

Java поддерживает как перегрузку методов, так и переопределение методов(другими словами, полиморфизм ), как две важные концепции объектно-ориентированного программирования. Они используются для достижения различных целей и имеют различные характеристики.

Это также часто задаваемый вопрос на собеседовании по Java. Давайте разберемся в различиях между перегрузкой и переопределением методов с помощью простых примеров.

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

Начнем с наглядного сравнения перегрузки методов и переопределения методов в Java:

Аспект Перегрузка метода Метод переопределения
Определение Определите несколько методов в одном классе с одинаковым именем, но разными параметрами. Предоставьте конкретную реализацию для метода, уже определенного в суперклассе, в подклассе с той же сигнатурой метода.
Наследование Не связано с наследованием; перегруженные методы определяются в том же классе и не наследуются. Тесно связано с наследованием; переопределенные методы определяются в суперклассе и могут наследоваться подклассами.
Тип возврата Могут иметь одинаковые или разные типы возврата. Должен иметь тот же возвращаемый тип или подтип в подклассе.
Метод сигнатуры Методы имеют одинаковое имя, но разные параметры(номер, тип или порядок). Методы имеют одинаковое имя, параметры и тип возвращаемого значения.
Полиморфизм Обеспечивает полиморфизм во время компиляции; правильный метод выбирается во время компиляции на основе сигнатуры метода. Обеспечивает полиморфизм во время выполнения; метод, который должен быть выполнен, определяется во время выполнения на основе фактического типа объекта.

Теперь давайте рассмотрим каждую концепцию более подробно.

2. Перегрузка метода

Перегрузка методов позволяет нам определять несколько методов в одном классе с одинаковым именем, но с разными параметрами(количество, тип или порядок параметров). Перегруженные методы могут иметь одинаковые или разные типы возвращаемых данных.

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

2.1 Аргументы метода должны быть разными

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

В следующем примере метод sum() определен дважды. Оба метода принимают два аргумента. Первый метод принимает аргументы типа Integer, а второй метод принимает аргументы типа Float и Integer. Здесь количество аргументов метода одинаково, но их типы различны.

 публичный класс Калькулятор {публичная сумма целого числа(целое число a, целое число b) {вернуть а + б;}публичная сумма целого числа(плавающая точка a, целое число b) {вернуть a.intValue() + b;}}

2.2 Типы возвращаемых значений методов не учитываются

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

В следующем примере методы принимают похожие аргументы, а их возвращаемые типы различаются. Это недопустимый код, который выдает ошибку компилятора «'sum(Integer, Integer)' уже определен в 'Calculator'».

 публичный класс Калькулятор {публичная сумма целого числа(целое число a, целое число b) {вернуть а + б;}публичная Двойная сумма(Целое число a, Целое число b) {вернуть новый Double(a + b);}} 
Недопустимая перегрузка

2.3. Вызванные исключения не рассматриваются.

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

 публичный класс Калькулятор {public Integer sum(Integer a, Integer b) выдает NullPointerException{вернуть а + б;}public Integer sum(Integer a, Integer b) выдает IllegalArgumentException{вернуть а + б;}}

3. Переопределение метода

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

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

Всегда помните эти правила при переопределении метода в Java.

3.1 Аргументы метода должны быть абсолютно одинаковыми

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

В следующем примере классы Parent и Child имеют методы с одинаковым именем и абсолютно одинаковым списком параметров. Это допустимое переопределение метода.

 класс Родитель {публичная сумма целого числа(целое число a, целое число b) {вернуть а + б;}}класс Child расширяет Parent {публичная сумма целого числа(целое число a, целое число b) {вернуть а + б;}}

3.2. Тип возвращаемого значения метода может быть подтипом в дочернем классе

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

В следующем примере тип возвращаемого значения метода в классе Parent — Number, а в классе Child — Integer. Это допустимый тип возвращаемого значения, поэтому он считается допустимым переопределением метода.

 класс Родитель {Сумма публичных чисел(Целое число a, Целое число b) {вернуть а + б;}}класс Child расширяет Parent {публичная сумма целого числа(целое число a, целое число b) {вернуть а + б;}}

Если мы используем несовместимый тип возвращаемого значения в классе Child, мы получим ошибку компилятора.

 класс Родитель {Сумма публичных чисел(Целое число a, Целое число b) {вернуть а + б;}}класс Child расширяет Parent {публичная строка сумма(Целое число a, Целое число b) {вернуть a.toString() + b.toString();}} 
 «sum(Integer, Integer)» в «Child» конфликтует с «sum(Integer, Integer)» в «Parent»; попытка использовать несовместимый возвращаемый тип

3.3 Выброшенное исключение может быть подтипом в дочернем классе

Переопределяющий метод не может выдать проверяемое исключение выше в иерархии, чем выданное переопределяемым методом.

Например, переопределенный метод в классе Parent выдает исключение FileNotFoundException, переопределяющий метод в классе Child может выдавать исключение FileNotFoundException; но ему не разрешено выдавать исключение IOException или Exception, поскольку исключение IOException или Exception находятся выше в иерархии.

 класс Родитель {public String readFile(String file) выдает FileNotFoundException {//...вернуть ноль;}}класс Child расширяет Parent {public String readFile(String file) выдает IOException {//...вернуть ноль;}}

В приведенном выше коде мы получим ошибку компилятора:

 «readFile(String)» в «Child» конфликтует с «readFile(String)» в «Parent»; переопределенный метод не выдает «java.io.IOException»

Обратите внимание, что мы можем опустить объявление исключения из переопределяющего метода. Это разрешено и совершенно допустимо. Кроме того, переопределяющий метод может выдать любое непроверенное(время выполнения) исключение, независимо от того, объявляет ли переопределяемый метод это исключение или нет.

В следующем примере переопределяющий метод пропускает FileNotFoundException и объявляет RuntimeException, которое является непроверяемым исключением. Это допустимое переопределение метода.

 класс Родитель {public String readFile(String file) выдает FileNotFoundException {//...вернуть ноль;}}класс Child расширяет Parent {public String readFile(String file) выдает RuntimeException {//...вернуть ноль;}}

3.4. Закрытые, статические и финальные методы не могут быть переопределены.

Методы private, static и final не могут быть переопределены в Java каким-либо образом.

  • Модификатор private ограничивает метод классом.
  • Статические члены принадлежат объекту класса.
  • Ключевое слово final используется с членами, которые не должны быть переопределены.

В следующем примере классы Parent и Child объявляют метод с одинаковым именем и параметрами, но это не переопределение метода. ReadFile() никогда не доступен за пределами класса Parent.

 класс Родитель {частный String readFile(файл строки){//...вернуть ноль;}}класс Child расширяет Parent {public String readFile(файл строки) {//...вернуть ноль;}}

3.5. Переопределение метода не может уменьшить область доступа

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

Например, если переопределенный метод в классе Parent защищен, то переопределяющий метод в классе Child не может быть закрытым. Он должен быть либо защищенным(тот же доступ), либо открытым(более широкий доступ).

 класс Родитель {защищенная строка readFile(файл строки){//...вернуть ноль;}}класс Child расширяет Parent {public String readFile(файл строки) {//...вернуть ноль;}}

4. Как проверить правильность переопределения метода

Чтобы проверить, правильно ли мы переопределяем метод или нет, просто используйте аннотацию @Override на переопределяющем методе в классе Child. Это проверит все правила переопределения методов. Если при переопределении возникнут какие-либо проблемы, это приведет к ошибке времени компиляции.

class Parent {protected String readFile(String file){//...return null;}}class Child extends Parent {@Overridepublic String readFile(String file) {//...return null;}}

Вот и все, что касается этой простой, но важной концепции, которая поможет вам освежить в памяти основы Java и объектно-ориентированного программирования.

5. Заключение

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

Прокрутить вверх