2.9. Опасная перегрузка
Если изменяется код в нескольких местах, ошибки тоже могут возникнуть в нескольких местах. Хуже, если изменился код в одном месте, а ошибки все равно возникли в нескольких местах. Это может быть следствием закона «дырявых» абстракций (в случае нарушения корректности, а не быстродействия, это означает недостаточную инкапсуляцию), но может иметь и другую причину, известную как «Broken dispatch» [11, 12].
Итак, для начала введем определения.
- Переопределением метода называется изменение реализации метода в подклассах.
- Перегрузкой метода называется объявление в одном классе нескольких методов с одним именем.
В отличие от переопределения, перегрузка разрешается статически: компилятор на стадии сборки программы определяет, какой метод вызывать. Это решение принимается на основе того, какие параметры передаются методу.
Пусть в классе Thing был такой метод (да, такой метод специально определять не нужно, но это только пример):
public boolean equals(Object other) { return super.equals(other); }
После чего в реализацию класса добавили перегруженный метод:
public boolean equals(Thing other) { return this.field == other.field; }
Начнем с того, что такой метод equals нарушает соглашения платформы Java (см. [12]), но это не главное. С того момента, как появился такой метод, стало трудно предсказать, какой из методов equals будет вызван в том или ином случае. Если ссылка на объект other имеет тип Thing, то будет вызываться новый метод, а если Object — старый. Таким образом, клиентский код был изменен без редактирования (некоторые вызовы стали связываться с другим методом). Это очень опасно.
Хорошее правило для перегрузки методов приведено в [12]: не определять несколько перегруженных методов с одинаковым числом параметров.
Кроме описанной проблемы, перегрузка может внести много путаницы, если клиент перепутает перегруженные версии методов. Часто лучше назвать методы по–разному, чем перегружать их.