Чем отличается перегрузка от переопределения?
Полиморфизм – соль ООП. Перегрузка (overload) и переопределение (override) – два инструмента достижения полиморфного поведения в Java.
Перегрузкой реализуется ad-hoc-полиморфизм. Это значит «один и тот же» метод может работать с разными параметрами. С технической точки зрения это просто два разных метода, сигнатуры которых имеют одинаковое название, но разный набор параметров. Важно помнить, что для перегрузки не достаточно различий только модификаторов, возвращаемых типов и списков исключений.
Ad-hoc – не совсем настоящий полиморфизм, так как при нём используется раннее, или статическое связывание (early binding, static dispatch). Это значит, что для выбора конкретного варианта метода используется информация о типе переменной, а не объекта в ней лежащего, и происходит это еще при компиляции.
Если в классе объявлены два перегруженных метода, а аргумент в вызове подходит под оба, случится ошибка компиляции. В примере ниже компилятор не может выбрать между вариантами метода println с параметром char[] и со String , так как null может быть и тем и другим.
Переопределение (override) дает полиморфизм подтипов. Это реализация/подмена метода нефинального родительского класса или интерфейса. С помощью этого механизма достигается поведение, когда экземпляр хранится под типом родителя, но реализация методов используется специфичная для этого конкретного подтипа. Пример:
List list = new LinkedList<>();
list.add(“foo“);
Здесь метод add вызывается общий для всех списков, но добавлен будет именно элемент связного списка.
Выбор конкретного метода происходит в последний момент, в процессе работы программы, в зависимости от типа объекта. Это называется позднее или динамическое связывание методов (late binding, dynamic dispatch).
Переопределение имеет непосредственное отношение к принципу подстановки Лисков (LSP): в хорошем объектно-ориентированном коде для вызывающего кода переопределенный метод не должен быть отличим от оригинального.
Переопределенный метод принято снабжать аннотацией @Override . Ее отсутствие допускается, но компиляция не перегружающего метода с такой аннотацией приведет к ошибке.
При переопределении можно сузить набор выбрасываемых исключений или тип результата, и заменить модификатор доступа на менее строгий.
Статические методы нельзя переопределить, можно только перегрузить.
О внутренностях процесса связывания можно почитать в этой статье.
Как правильно: перегрузка или переопределение?
Чтобы далеко не ходить за примерами я стал искать прямо на сайте ответы по интересующим меня методам Equals и OnModelCreating, которые описаны в документации как виртуальные, и нашел следующие ответы:
- «следует перегрузить метод OnModelCreating — и написать там примерно следующее»
27 май ’15 в 15:53, Pavel Mayorov, 9,255 - «базовый класс всех типов-значений, перегружает метод Equals»
30 сен ’15 в 0:01, VladD, 74k - «Переопределите Equals так, чтобы он сравнивал два экземпляра»
23 дек ’15 в 8:52, PashaPash, 20.1k
Из контекста следует, что в ответах говорят о виртуальных методах.
Получается, что перегрузка и переопределение — это одно и тоже.
Хотелось бы понять как правильно: перегрузка или переопределение?
UPD: (2/10/2016 8:52 PM)
Перегрузка и переопределение, как сказал rdom: это абсолютно разные вещи.
VladD внес исправления в свой ответ.
Ошибки в других ответах, надеюсь, также будут исправлены.
Отслеживать
задан 9 фев 2016 в 20:36
203 2 2 серебряных знака 11 11 бронзовых знаков
1 ответ 1
Сортировка: Сброс на вариант по умолчанию
По смыслу — это абсолютно разные вещи.
Перегрузка (Overloading) — это возможность использовать одинаковые имена в пределах одного класса у методов сходных по своей сути, но не реализации,за счет расширения понятия имени метода до сигнатуры. Подробнее в соседнем вопросе
Переопределение (Overriding) — относится к виртуальным и абстрактным методам. Это возможность замены виртуального или реализация абстрактного наследуемого метода базового класса методом производного класса.
class A < virtual void SomeMethod(int par1, double par2) < >//Делаем перегруженный вариант метода для другого набора параметров void SomeMethod(double par1, int par2) <> > class B : A < //Переопределяем унаследованный виртуальный метод override void SomeMethod(int par1, double par2) < >>
UPD
в наследниках или в производных?
тут разночтений нет. Производный класс, класс наследник, а также принятые в Java суб-класс или подкласс, являются синонимами. Также синонимами являются базовый класс, класс предок, супер-класс(java) и над-класс(java).
Дабы не плодить споры: я использую перевод принятый в MSDN. В различных источниках могут использоваться другие варианты перевода. В любом случае, первичным остается смысл оригинальных терминов на английском.
Переопределение и перегрузка в Java
Переопределение (overriding) и перегрузка (overloading) – одни из ключевых понятий в программировании на Java. Эти механизмы позволяют реализовать полиморфизм в программах Java. Полиморфизм — одна из концепций ООП.
На этом скриншоте показано, где в коде Java происходит перегрузка, а где – переопределение.
Переопределением называют случаи, когда сигнатура метода (имя и параметры) в суперклассе и дочернем классе совпадают. Когда два или более метода в одном классе имеют одинаковое имя, но разные параметры, это называется перегрузкой.
Перегрузка и переопределение: сравнительный анализ
| Переопределение | Перегрузка |
| Реализует «полиморфизм времени выполнения» | Реализует «полиморфизм времени компиляции» |
| Вызов метода определяется во время выполнения на основе типа объекта. | Вызов метода определяется во время компиляции |
| Происходит между суперклассом и подклассом | Происходит между методами в рамках одного класса |
| Одинаковая сигнатура (имя и аргументы метода) | Одинаковое имя, но разные параметры |
| В случае возникновения ошибки эффект будет виден во время выполнения | Ошибка может быть обнаружена во время компиляции. |
Переопределение и перегрузка: примеры
Давайте рассмотрим следующие примеры, чтобы понять, как работает переопределение и перегрузка в программах Java. За основу возьмем такой код:
package com.journaldev.examples; import java.util.Arrays; public class Processor < public void process(int i, int j) < System.out.printf("Processing two integers:%d, %d", i, j); >public void process(int[] ints) < System.out.println("Adding integer array:" + Arrays.toString(ints)); >public void process(Object[] objs) < System.out.println("Adding integer array:" + Arrays.toString(objs)); >> class MathProcessor extends Processor < @Override public void process(int i, int j) < System.out.println("Sum of integers is " + (i + j)); >@Override public void process(int[] ints) < int sum = 0; for (int i : ints) < sum += i; >System.out.println("Sum of integer array elements is " + sum); > >
Переопределение
Метод process() и параметры int i, int j в Processor переопределяются дочерним классом MathProcessor. Обратите внимание на строки 7 и 23:
public class Processor public void process(int i, int j) < /* . */ > > /* . */ class MathProcessor extends Processor @Override public void process(int i, int j) < /* . */ > >
Метод process() и int[] ints в Processor также переопределяются в дочернем классе. Смотрите строки 11 и 28.
public class Processor public void process(int[] ints) < /* . */ > > /* . */ class MathProcessor extends Processor @Override public void process(Object[] objs) < /* . */ > >
Перегрузка
Метод process() перегружается в классе Processor. Об этом говорят строки 7, 11 и 15:
public class Processor public void process(int i, int j) < /* . */ > public void process(int[] ints) < /* . */ > public void process(Object[] objs) < /* . */ > > Итоги
В этом руководстве мы на простом примере рассмотрели переопределение и перегрузку в Java. Переопределение происходит, когда сигнатура метода в суперклассе и дочернем классе одинакова. Перегрузка возникает, когда два или более метода в одном классе имеют одинаковое имя, но разные параметры.
Чем перегрузка методов отличается от переопределения методов
Статья очень хорошая, но в корне не согласен с утверждением относительно того, что модификатор доступа у переопределенного метода также не может отличаться от «оригинального». Это утверждение справедливо лишь в том случае, если модификатор доступа переопределяемого метода изначально был public. Суть заключается в том, что модификатор доступа переопределённого метода (в классе-наследнике) всегда должен быть не менее открытым, чем в базовом классе. Если мы переопределяем protected-метод, то можем сделать наш переопределённый метод либо protected, либо же public. Если же метод имел уровень доступа package-private (доступ по умолчанию, в пределах пакета, в котором располагается класс), то в классе-потомке после переопределения метод может иметь модификаторы package-private, protected или public.
Ислам Уровень 32
8 июня 2023
4 апреля 2023
Помогите разобраться: если мы не используем переопределение. срабатывает полиморфизм и медведь, например, все равно рычит. ♂️
Василий Уровень 25
2 апреля 2023
Кто вернулся после задачи, где изначальный метод возвращает DBObject, а переопределенный должен возвращать User. Не удивляйтесь, статья устарела, в комментах ниже все объясняют. — Если метод в базовом классе возвращает void, переопределенный метод должен возвращать void — Если метод в базовом классе возвращает примитив, переопределенный метод должен возвращать тот же примитив — Если метод в базовом классе возвращает определенный тип, переопределенный метод должен возвращать тот же тип или подтип (он же ковариантный возвращаемый тип)
BESTrOFF Уровень 32
22 марта 2023
На сегодняшний день @Override, вроде, не обязательно писать для компилятора, скорее для того, кто будет читать код.
