Опирается на правила: JS-4.1JS-4.7 из Java Style Guide → раздел 4. Выражения.

Важно знать

  • Сложность булева — не более 3 операторов &&/|| в одном выражении.
  • Java-стиль массивов: int[] nums, не int nums[].
  • Порядок модификаторов: public/protected/private → static → final → transient → volatile → synchronized.
  • Не указывать неявные: public abstract в методах интерфейса; static во вложенных enum.
  • Method reference вместо лямбды, где имеет смысл (list::contains).
  • Большие лямбды (> 1 выражения) — вынести в named method.
  • Guard expression вместо вложенных условий — early return.

Выражения — место, где «работает, но плохо читается» накапливается. UCP формулирует правила так, чтобы выражение сразу говорило о намерении — early return, method reference, явный порядок модификаторов.

Сложность булева

JS-4.1: ≤ 3 операторов.

// ✓ — 3 оператора
boolean valid = (!a && b) | (a || !b) ^ a;

// ✗ — 4 оператора, нечитаемо
boolean bad = (a && b) && c && (c || b);

3 операторов = декомпозируем в named-переменные или выносим в method:

// ✓ — named-переменные
var ageValid = age >= 18 && age <= 65;
var statusValid = status == Status.ACTIVE || status == Status.PENDING;
var locationValid = country.equals("RU") || country.equals("BY");

if (ageValid && statusValid && locationValid) {
    // ...
}

// или в method
private boolean isEligibleForBonus(User user) {
    if (user.age() < 18 || user.age() > 65) return false;
    if (user.status() != Status.ACTIVE && user.status() != Status.PENDING) return false;
    return user.country().equals("RU") || user.country().equals("BY");
}

Java-стиль массивов

JS-4.2: тип имеет квадратные скобки.

int[] nums;                             // ✓
String[] strs;                          // ✓
List<int[]> grids;                      // ✓

String strs[];                          // ✗ — C-стиль
int nums[][];                           // ✗ — C-стиль

int[] — это тип «массив int-ов». int nums[] — устаревший C-style, где [] ассоциируется с переменной. В Java тип — целостная единица.

Порядок модификаторов

JS-4.3: фиксированный.

public → protected → private → static → final → transient → volatile → synchronized
public static final String TIMEOUT = "30s";           // ✓
private static final Logger log = ...;                 // ✓
protected static final int MAX = 100;                  // ✓

static public final String TIMEOUT = "30s";            // ✗ — порядок нарушен
final public static String TIMEOUT = "30s";            // ✗

JLS не требует, но конвенция помогает быстрее сканировать declarations. IntelliJ имеет inspection «Wrong modifier order» — fix через Alt+Enter.

Не указывать неявные модификаторы

JS-4.4: не дублировать defaults.

public interface OrderService {
    void create(CreateOrderCommand command);   // ✓ — public abstract неявно

    public abstract void create(CreateOrderCommand command);   // ✗
}

public interface OrderStatus {
    enum Type { DRAFT, CONFIRMED }              // ✓ — static неявно во вложенных enum

    static enum Type { DRAFT, CONFIRMED }       // ✗
}

Контекст:

  • Методы интерфейсаpublic abstract по умолчанию (для regular methods, не default/static).
  • Вложенные enum/interfacestatic по умолчанию.
  • Поля интерфейсаpublic static final по умолчанию.

Не пишем явно — это шум.

Method reference vs лямбда

JS-4.5: short когда возможно.

list.stream()
    .filter(someStrings::contains)                  // ✓ — method reference
    .forEach(System.out::println);                  // ✓

list.stream()
    .filter(s -> someStrings.contains(s))           // ✗ — лямбда дублирует method
    .forEach(s -> System.out.println(s));           // ✗

Method reference — компактнее, выразительнее. IntelliJ предлагает auto-convert.

Когда не используем method reference:

  • Лямбда делает что-то дополнительное к method call: s -> someStrings.contains(s.toLowerCase()) — reference не подходит.
  • Лямбда вызывает несколько методов: s -> { validate(s); process(s); }.

Большие лямбды → named method

JS-4.6: > 1 выражения.

// ✗ — большая лямбда inline
list.stream()
    .filter(order -> {
        if (order.status() != OrderStatus.CONFIRMED) return false;
        var diff = Duration.between(order.createdAt(), Instant.now());
        return diff.toDays() > 7 && order.totalAmount().compareTo(BigDecimal.ZERO) > 0;
    })
    .forEach(this::process);

// ✓ — выделили в method, передаём reference
list.stream()
    .filter(this::isStaleOrderWithAmount)
    .forEach(this::process);

private boolean isStaleOrderWithAmount(Order order) {
    if (order.status() != OrderStatus.CONFIRMED) return false;
    var ageInDays = Duration.between(order.createdAt(), Instant.now()).toDays();
    return ageInDays > 7 && order.totalAmount().compareTo(BigDecimal.ZERO) > 0;
}

Преимущества:

  • Имя объясняет назначение (isStaleOrderWithAmount).
  • Testable — method можно unit-test-ить.
  • Reuse — same logic из другого места.
  • Stack trace — exception показывает имя method, не lambda$lambda$...

Guard expression

JS-4.7: early return.

// ✓ — guard pattern, early return на ошибки
public void process(Order order) {
    if (!order.isValid()) {
        throw new InvalidOrderException(order.id());
    }
    if (order.status() == OrderStatus.CANCELLED) {
        throw new OrderCancelledException(order.id());
    }

    doProcess(order);
    publish(order);
    notify(order);
}

// ✗ — nested else с throw
public void process(Order order) {
    if (order.isValid()) {
        if (order.status() != OrderStatus.CANCELLED) {
            doProcess(order);
            publish(order);
            notify(order);
        } else {
            throw new OrderCancelledException(order.id());
        }
    } else {
        throw new InvalidOrderException(order.id());
    }
}

Guard expression — все check-условия наверху method-а с early return / throw, основная логика — без вложенности. Это:

  • Снижает максимальную indentation на 2 уровня.
  • Делает «happy path» очевидным внизу.
  • Каждый guard — отдельный assertion, легко добавить новый.

Применяется к public-методам где входные данные могут быть невалидными. Для internal methods — guard опционален.

Что запрещено

АнтипаттернПравилоЧто взамен
Boolean expression > 3 операторов inlineJS-4.1named-переменные или method
int nums[] C-стильJS-4.2int[] nums
Произвольный порядок модификаторовJS-4.3public static final ...
public abstract в interface methodJS-4.4без неявных модификаторов
s -> list.contains(s)JS-4.5list::contains
Большая inline-лямбдаJS-4.6named method + reference
Nested if-else с throw в elseJS-4.7guard expression + early return
if (...) { return; } else if (...) ...JS-4.7early return без else
Boolean expression с && и || без скобокJS-4.1явные скобки

Куда дальше

  • Java → раздел 4. Выражения — нормативные формулировки.
  • Именование — имена методов для extracted lambdas.
  • Современные фичи Java — switch expressions, record patterns.
  • Lombok — @RequiredArgsConstructor убирает явный ctor.
  • Отступы и форматирование — длина строки, перенос.
  • Error handling → exception hierarchy — для guard expressions.