Методы Java

Объявление

Метод задаёт именованную операцию класса:

public static BigDecimal calculateTotal(
        List<BigDecimal> prices,
        BigDecimal discount
) throws IllegalArgumentException {
    // тело
}

В объявлении могут быть:

  1. аннотации;
  2. модификаторы доступа и static, final, abstract, synchronized;
  3. параметры типа;
  4. возвращаемый тип или void;
  5. имя;
  6. список параметров;
  7. throws;
  8. тело либо ; у абстрактного метода.

Сигнатура метода в Java — имя и типы параметров. Возвращаемый тип, имена параметров и throws в сигнатуру не входят, поэтому нельзя объявить два метода, отличающихся только результатом.

Возвращаемое значение

return завершает текущий вызов и при необходимости возвращает значение:

Optional<User> findActive(List<User> users, long id) {
    for (User user : users) {
        if (user.id() == id && user.active()) {
            return Optional.of(user);
        }
    }
    return Optional.empty();
}

У void-метода допустим return; для раннего выхода. Код после безусловного return недостижим и не компилируется.

Передача аргументов

Java всегда передаёт аргументы по значению.

  • Для примитива копируется само число/boolean/char.
  • Для объекта копируется значение ссылки.
void rename(User user) {
    user.setName("new"); // меняет общий объект
    user = new User("other"); // меняет только локальную копию ссылки
}

Метод не может переassignить переменную вызывающего кода, но может изменить доступный через ссылку mutable-объект.

Методы экземпляра и static

Метод экземпляра вызывается относительно объекта и имеет неявную ссылку this. static принадлежит классу и не может напрямую обращаться к нестатическим полям без конкретного объекта.

final class Temperature {
    private final double celsius;
 
    double celsius() {
        return celsius;
    }
 
    static Temperature ofCelsius(double value) {
        return new Temperature(value);
    }
}

Статическая фабрика может иметь имя, вернуть кэшированный объект или подтип; конструктор всегда создаёт экземпляр объявленного класса.

Varargs

int sum(int... values) {
    return Arrays.stream(values).sum();
}

Внутри values — обычный массив. Varargs должен быть последним параметром. Вызов обычно создаёт массив, а передача null неоднозначна и опасна. Не стоит использовать varargs на горячем пути без оценки аллокаций.

Контракт метода

Полезно явно понимать:

  • допустимые входные значения;
  • возвращаемый результат;
  • возможные исключения;
  • побочные эффекты;
  • требования к потокобезопасности и транзакции;
  • сложность.
/**
 * Возвращает долю в диапазоне [0, 1].
 * @throws IllegalArgumentException если total <= 0
 */
double ratio(long part, long total) {
    if (total <= 0) {
        throw new IllegalArgumentException("total must be positive");
    }
    return (double) part / total;
}

Ловушки

  • Считать, что объект передаётся «по ссылке», и ожидать переassign переменной вызывающего кода.
  • Делать метод одновременно вычислением, записью в БД и отправкой сообщения без явного контракта.
  • Возвращать внутреннюю mutable-коллекцию.
  • Злоупотреблять static, скрывая зависимости и состояние.
  • Делать длинный список boolean-параметров: вызов configure(true, false, true) ничего не объясняет.

Вопросы на собеседовании

  1. Что входит в сигнатуру метода Java?
  2. Передаёт ли Java объекты по ссылке?
  3. Чем метод экземпляра отличается от static-метода?
  4. Как varargs представлен внутри метода?
  5. Можно ли перегрузить метод только возвращаемым типом?
  6. Что составляет практический контракт метода?

Связанные заметки

Сигнатура и контракт метода · Перегрузка Java · Переопределение Java · Конструкторы Java · Класс Optional · Работа с исключениями в Java