Сигнатура и контракт метода

В практическом смысле сигнатура Java-метода включает имя и типы формальных параметров; для generic-методов правила также учитывают параметры типа и erasure. Возвращаемый тип, имена параметров, модификаторы и throws не позволяют объявить вторую перегрузку с той же сигнатурой.

int parse(String value) { return 0; }
// long parse(String text) { return 0; } // конфликт сигнатур
 
int parse(byte[] value) { return 0; }    // допустимая перегрузка

Covariant return разрешён при override:

class Parent {
    Number value() { return 1; }
}
class Child extends Parent {
    @Override
    Integer value() { return 1; }
}

Контракт

Контракт шире объявления метода:

  • предусловия для аргументов и состояния;
  • постусловия результата;
  • изменяемое состояние и другие side effects;
  • допустимые исключения;
  • гарантии thread safety, blocking и идемпотентности;
  • поведение для null, пустых данных и границ.
/**
 * Резервирует amount единиц.
 * @throws IllegalArgumentException если amount <= 0
 * @throws InsufficientStockException если остатка недостаточно
 * Не меняет остаток при ошибке.
 */
Reservation reserve(int amount) { ... }

throws перечисляет checked exceptions декларации, но это лишь часть контракта. Unchecked exception тоже может быть осознанным условием API.

Ошибки

  • считать возвращаемый тип частью сигнатуры перегрузки;
  • менять в наследнике допустимые входы так, что клиент базового типа ломается;
  • не описывать side effect или повторный вызов;
  • обещать «никогда null», а на редком пути вернуть null.

На собеседовании

Можно ли перегрузить метод только возвращаемым типом? Нет.

Входит ли throws в сигнатуру для перегрузки? Нет.

Что такое контракт кроме Java-декларации? Условия, результат, исключения и наблюдаемые эффекты.

Можно ли сузить возвращаемый тип при override? Да, ковариантно.

Связано: Методы Java, Перегрузка Java, Переопределение Java, Полиморфизм.