✅ Гибкие конструкторы

JDK 25
JDK 25

JDK 25 привнесёт ещё одно небольшое, но важное, на мой взгляд, изменение в язык программирования Java - гибкие конструкторы (JEP 513). Цель изменения - позволить выполнять действия в конструкторе класса-наследника до вызова конструктора родительского класса.

Представим себе достаточно банальную ситуацию: нам нужно создать экземпляр некоторого класса-наследника, но в процессе создания нужно провалидировать переданные в аргументах данные. До Java 25 сделать это можно только после вызова конструктора родительского класса:

class Vehicle {
  int wheels;
  Vehicle(int wheels) {
    this.wheels = wheels;
  }
}

class Car extends Vehicle {
  Car(int wheels) {
    super(wheels);
    // Всякие Reliant-ы и Morgan Super 3 - 
    // это трайки и автоциклы
    if (wheels < 4) {
      throw new IllegalArgumentException();
    }
  }
}

Проблема такого кода заключается в том, что в конструкторе родительского класса может находиться какая-то ресурсоёмкая логика, которая будет выполнена зря. Да, валидацию можно вынести в дополнительные методы класса, но выглядеть это будет не всегда красиво и удобно:

class Vehicle {
  int wheels;
  Vehicle(int wheels) {
    this.wheels = wheels;
  }
}

class Car extends Vehicle {
  Car(int wheels) {
    super(wheelsMustBeAtLeastFour(wheels));
  }
  
  static int wheelsMustBeAtLeastFour(int wheels) {
    if (wheels < 4) {
      throw new IllegalArgumentException();
    }

    return wheels;
  }
}

Это будет так, даже если попытаться шаблонизировать код:

class Assertions {

  static <T, E extends RuntimeException> T require(T value,
                                                   Predicate<T> predicate, 
                                                   Supplier<E> exceptionSupplier) {
    if (predicate.test(value)) {
      return value;
    }

    throw exceptionSupplier.get();
  }
}

class Vehicle {
  int wheels;
  Vehicle(int wheels) {
    this.wheels = wheels;
  }
}

class Car extends Vehicle {
  Car(int wheels) {
    super(Assertions.require(wheels, v -> v >= 4, IllegalArgumentException::new));
  }
}

С Java 25 необходимые манипуляции с аргументами можно выполнять до вызова конструктора родительского класса:

class Vehicle {
  int wheels;
  Vehicle(int wheels) {
    this.wheels = wheels;
  }
}

class Car extends Vehicle {
  Car(int wheels) {
    if (wheels < 4) {
      throw new IllegalArgumentException();
    }
    super(wheelsMustBeAtLeastFour(wheels));
  }
}

На мой взгляд, выглядит красивее и проще. Но не стоит забывать о том, что до вызова конструктора класса-родителя нельзя обращаться к this, методам и инициализированным свойствам текущего экземпляра.

51 views·2 shares