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]: не определять несколько перегруженных методов с одинаковым числом параметров.

Кроме описанной проблемы, перегрузка может внести много путаницы, если клиент перепутает перегруженные версии методов. Часто лучше назвать методы по–разному, чем перегружать их.