Полиморфизм – соль ООП. Перегрузка (overload) и переопределение (override) – два инструмента достижения полиморфного поведения в Java.
Перегрузкой реализуется
ad-hoc-полиморфизм. Это значит «один и тот же» метод может работать с разными параметрами. С технической точки зрения это просто два разных метода, сигнатуры которых имеют одинаковое название, но разный набор параметров. Важно помнить, что для перегрузки
не достаточно различий
только модификаторов, возвращаемых типов и списков исключений.
Ad-hoc – не совсем настоящий полиморфизм, так как при нём используется
раннее, или
статическое связывание (early binding,
static dispatch). Это значит, что для выбора конкретного варианта метода используется информация о типе
переменной, а не объекта в ней лежащего, и происходит это еще при компиляции.
Если в классе объявлены два перегруженных метода, а аргумент в вызове подходит под оба, случится ошибка компиляции. В примере ниже компилятор не может выбрать между вариантами метода
println
с параметром
char[]
и со
String
, так как
null
может быть и тем и другим.
Переопределение (override) дает
полиморфизм подтипов. Это реализация/подмена метода нефинального родительского класса или интерфейса. С помощью этого механизма достигается поведение, когда экземпляр хранится под типом родителя, но реализация методов используется специфичная для этого конкретного подтипа. Пример:
List<String> list = new LinkedList<>();
list.add(“foo“);
Здесь метод add вызывается общий для всех списков, но добавлен будет именно элемент
связного списка.
Выбор конкретного метода происходит в последний момент, в процессе работы программы, в зависимости от типа объекта. Это называется
позднее или
динамическое связывание методов (
late binding,
dynamic dispatch).
Переопределение имеет непосредственное отношение к
принципу подстановки Лисков (LSP): в хорошем объектно-ориентированном коде для
вызывающего кода переопределенный метод не должен быть отличим от оригинального.
Переопределенный метод принято снабжать аннотацией
@Override
. Ее отсутствие допускается, но компиляция не перегружающего метода с такой аннотацией приведет к ошибке.
При переопределении можно сузить набор выбрасываемых исключений или тип результата, и заменить модификатор доступа на менее строгий.
Статические методы нельзя переопределить, можно только перегрузить.
О внутренностях процесса связывания можно почитать в
этой статье.