Руководство Google по форматированию кода на Java. Часть 2

4 Форматирование

Терминология
Тело класса, метода, конструктора или оператора switch относится к блочной конструкции.
Обратите внимание, что согласно Разделу 4.8.3.1, любой инициализатор массива также может рассматриваться как блочная конструкция.

4.1 Фигурные скобки

4.1.1 Фигурные скобки используются везде
Фигурные скобки используются с операторами if, else, for, while и do-while, даже если их тело пустое или содержит только одну строку кода.
Другие необязательные фигурные скобки, например в лямбда-выражении, остаются необязательными.
4.1.2 Непустые блоки: стиль K & R
Для непустых блоков и блочных конструкций фигурные скобки («Египетские скобки») ставятся в стиле Кернигана и Ритчи (для наглядности мы решили добавить немного кода, демонстрирующего данные правила — примечание переводчика):

  • Перед открывающейся скобкой переход на новую строку не делается (кроме случаев, описанных далее):
// правильно
if (true) {

// неправильно
if (true)
{
  • Переход на новую строку делается после открывающей скобки:
// правильно
while (true) {
    // some code

// неправильно
while (true) { // some code
  • Переход на новую строку делается перед закрывающей скобкой:
// правильно
for (true) {
    // some code
}

// неправильно
while (true) { /* some code */ }

// неправильно
if (true) {
    /* some code */ }
  • Переход на новую строку делается после закрывающей скобки, только если эта скобка завершает оператор или тело метода, конструктора или именованного (обычного) класса. Переход на новую строку не делается после скобки, если за ней следует else, catch или точка с запятой
Примеры правильно примененных правил:
return () -> {
    while (condition()) {
        method();
   }
};

return new MyClass() {
    @Override public void method() {
        if (condition()) {
            try {
                something();
            } catch (ProblemException e) {
                recover();
            }
        } else if (otherCondition()) {
            somethingElse();
        } else {
            lastThing();
        }
        {
            int x = foo();
            frob(x);
        }
    }
};
Некоторые исключения для перечислений приведены в Разделе 4.8.1
4.1.3 Пустые блоки могут быть сжатыми
Пустой блок или пустая блочная конструкция может быть оформлена в стиле K & R (как описано в Разделе 4.1.2). Также возможно, чтобы такой блок был закрыт сразу после открытия, без символов или разрыва строки внутри {}. Это правило не относится к случаю, когда блок является частью многоблочного оператора, который содержит if-else или try-catch-finally.

Примеры:
// Приемлемо
void doNothing() {}

// Также приемлемо
void doNothingElse() {
}

// Неприемлемо: нельзя использовать пустые блоки в многоблочных операторах
try {
    doSomething();
} catch (Exception e) {}

4.2 Два пробела для отступа

Каждый раз, когда открывается новый блок или блочная конструкция, отступ вправо увеличивается на два пробела (лучше на четыре — примечание переводчика). Когда блок заканчивается, начало следующей строки кода смещается на предыдущий уровень смещения. Уровень смещения применяется как к коду, так и к комментариям внутри блока (см. пример в Разделе 4.1.2).

4.3 Один оператор на каждую строку

Каждый оператор завершается переходом на новую строку.

4.4 Ограничение длины строки в 100 символов

Код на Java имеет ограничение на длину строки в 100 символов (из-за распространения широкоформатных мониторов, допустимой шириной является использование до 120 символов — примечание переводчика). Под «символом» понимается любой из элементов Unicode. За исключением случаев, описанных ниже, каждая строка, превышающая это ограничение, должна быть перенесена, как описано в Разделе 4.5.
Исключения:
  1. Строки, в которых соблюдение ограничения невозможно (например, длинный URL в Javadoc или длинная ссылка на метод JSNI)
  2. Объявления package и import (см. Разделы 3.2 и 3.3)
  3. Содержимое текстовых блоков
  4. Строки с командами в комментариях, которые могут быть скопированы и вставлены для выполнения в терминале
  5. Очень длинные идентификаторы, в редких случаях, когда они необходимы, могут превышать ограничение на длину строки. В таком случае допустимый перенос для окружающего кода должен соответствовать результату работы google-java-format.

4.5 Перенос строк

Терминология
Действие, когда код мог бы быть размещен на одной строке, но разделяется на несколько строк, называется переносом строк.
Не существует общепринятого правила, определяющего, как именно необходимо выполнять перенос строки в каждой ситуации. Зачастую один и тот же фрагмент кода можно перенести несколькими допустимыми способами.
Обычно перенос делается с целью избежания превышения допустимой длины строки. Но даже код, умещающийся в это ограничение, по решению автора, может быть перенесен на новую строку.
Совет: выделение вспомогательного метода или локальной переменной может решить проблему без переноса строк кода
4.5.1 Где выполнять перенос
Главное правило переноса строк: предпочитайте разрыв на более высоком синтаксическом уровне. Также:
1. Если строка разрывается не на операторе присваивания, то разрыв следует выполнить перед символом оператора.
Это правило также применимо к следующим «оператороподобным» символам:
  • точка-разделитель ( . )
  • два двоеточия в ссылке на метод ( :: )
  • амперсанд в скобках дженерика <T extends Foo & Bar>
  • вертикальная черта в блоке catch
catch (FooException | BarException e)
2. При разрыве строки на операторе присваивания разрыв обычно ставится после символа, но допустим и разрыв до него. Это также относится к двоеточию в цикле for-each.
3. Имя метода, конструктора или класса-записи при переносе строки остается присоединенным к открывающей скобке «(»
4. Запятая ( , ) при переносе строки остается с предшествующим ей элементом
5. Строка никогда не разрывается рядом со стрелкой в ​​правиле лямбда или switch, за исключением того, что разрыв может следовать сразу за стрелкой, если текст, следующий за ней, состоит из одного не заключенного в скобки выражения:
MyLambda<String, Long, Object> lambda =
        (String label, Long value, Object obj) -> {
            ...
        };

Predicate<String> predicate = str ->
        longExpressionInvolving(str);

switch (x) {
    case ColorPoint(Color color, Point(int x, int y)) ->
            handleColorPoint(color, x, y);
    ...
}
Примечание: основная цель переноса строки — создание понятного кода, а не кода с наименьшим количеством строк
4.5.2 Отступ продолжения строки на 4 и более пробелов
При переносе строки каждая следующая ее строка-продолжение смещается вправо как минимум на 4 пробела относительно исходной.

Когда продолжений строки несколько, отступ может варьироваться сверх 4 пробелов по желанию автора. По общему правилу, две подстроки могут иметь одинаковый отступ тогда и только тогда, когда они начинаются с синтаксически параллельных элементов.

В Разделе 4.6.3 рассматривается нерекомендуемая практика использования различного количества пробелов для выравнивания элементов кода относительно предыдущих строк.

4.6 Отступы

4.6.1 Пустые строки
Одна пустая строка всегда ставится:

1. Между расположенными друг за другом членами или инициализаторами класса: полями, конструкторами, методами, вложенными классами, статическими и динамическими блоками инициализации

  • исключение: пустая строка между двумя последовательными полями (между которыми нет другого кода) необязательна. При необходимости пустые строки используются для логического группирования полей
  • исключение: пустые строки между константами перечисления (см. Раздел 4.8.1)
2. В соответствии с другими разделами данного документа (например, Раздел 3 и Разделом 3.3)
Пустая строка также может использоваться в любом месте для повышения читаемости кода, например, между операторами для организации кода в логические блоки. Пустая строка перед первым членом класса, или блоком инициализации, или после последнего члена, или блока инициализации класса не приветствуется, но и не запрещается.
Допускаются несколько последовательных пустых строк, но они никогда не требуются (и не поощряются).
4.6.2 Пробелы
Помимо требований самого языка или прочих правил данного документа, а также не считая литералов, комментариев и Javadoc, одиночные пробелы из таблицы ASCII могут ставиться только в следующих местах:
  1. Между любым ключевым словом (например, if, for или catch) и открывающей скобкой (, которая следует за ним на той же строке
  2. Между любым ключевым словом (например, else или catch) и закрывающей фигурной скобкой }, которая предшествует ему на той же строке
  3. Перед любой открывающей фигурной скобкой {, за исключением:
  • @SomeAnnotation({a, b})
  • String[][] x = {{"foo"}}; - пробел между {{ не требуется согласно п. 9 ниже
4. С обеих сторон любого бинарного или тернарного оператора. Это также относится к следующим «оператороподобным» символам:
  • амперсанд внутри угловых скобок: <T extends Foo & Bar>
  • вертикальная черта в блоке catch, разделяющая несколько исключений: catch (FooException | BarException e)
  • двоеточие ( : ) в  for-each
  • стрелка в лямбда-выражении: (String str) -> str.length() или стрелка в switch: case "FOO" -> bar();"
Но это правило не применимо к операторам:
  • два двоеточия ( :: ) в ссылке на метод, которые пишутся как Object::toString
  • точка-разделитель ( . ), которая пишется как object.toString()
5. После ( , : ; ) или закрывающей круглой скобки ) при приведении типа

6. Между любым содержимым и двойным слэшем ( // ), начинающим комментарий. Допускается несколько пробелов

7. Между двойным слэшем ( // ), начинающим комментарий, и текстом комментария. Допускается несколько пробелов

8. Между типом и идентификатором в объявлении: List<String> list

9. Опционально внутри скобок инициализатора массива. Оба варианта допустимы: new int[] {5, 6} и new int[] { 5, 6 }

10. Между аннотацией типа и [] или ...
Это правило никогда не следует трактовать как требование или запрет на дополнительные пробелы в начале или конце строки; оно относится лишь к внутренним пробелам.
4.6.3 Горизонтальное выравнивание никогда не требуется
Терминология
Горизонтальное выравнивание — это практика добавления различного числа дополнительных пробелов в вашем коде с целью размещения определенных элементов под другими элементами с предыдущей строки.
Эта практика разрешена, но ее использование не требуется данным руководством. Также нет необходимости поддерживать соблюдение выравнивания в тех участках кода, где оно уже было применено.

Пример без выравнивания и с ним:
private int x;       // хорошо
private Color color; // и это тоже

private int   x;      // разрешено, но при редактировании в будущем
private Color color;  // можно оставить без выравнивания
Совет: выравнивание повышает читабельность, но создает проблемы с поддержкой такого кода в будущем. Допустим, требуется изменить только одну строку. Это изменение может исказить форматирование ранее написанного кода, что допускается. Но скорее всего, программист начнет корректировку пробелов, что может запустить каскад из бессмысленного труда. Или, в лучшем случае, приведет к искажению информации в истории версий, замедлит ревьюеров и усугубит конфликты слияния. Эти практические соображения имеют приоритет над выравниванием.
4.7 Группирующие скобки рекомендованы
Допускается не ставить группирующие скобки только тогда, когда автор кода и ревьюер пришли к общему согласию, что код будет неверно истолкован без скобок, и при этом они не облегчили бы его чтение. Неразумно полагать, что каждый, кто читает код, помнит наизусть всю таблицу приоритетов использования операторов Java.

4.8 Конкретные конструкции

4.8.1 Классы-перечисления
После каждой запятой, следующей за константой перечисления, перенос строки необязателен. Дополнительные пустые строки (обычно одна) также допускаются. Вот один из возможных вариантов:
private enum Answer {
    YES {
        @Override public String toString() {
            return "yes";
        }
    },

    NO,
    MAYBE
}
Код класса-перечисления без методов и документации, описывающих его константы, может быть отформатирован, как инициализатор массива (см. Раздел 4.8.3.1):
private enum Suit { CLUBS, HEARTS, SPADES, DIAMONDS }
Так как перечисления — это классы, для них следует применять все прочие правила форматирования классов.
4.8.2 Объявления переменных
4.8.2.1 Одна переменная — одно объявление
Каждая переменная (поле или локальная) объявляется только по одной за раз: объявления вида int a, b; не используются.
Исключение: множественные объявления переменных допускаются в заголовке цикла for.
Исключение: Множественные объявления переменных допускаются в заголовке цикла for.
4.8.2.2 Объявляйте переменные в месте их использования
Локальные переменные не принято объявлять в начале содержащего их блока или блочной конструкции. Наоборот, локальные переменные необходимо объявлять максимально близко к месту их первого использования (в разумных пределах), чтобы минимизировать их область видимости. Обычно локальные переменные инициализируются в момент объявления, или же сразу после него.
4.8.3 Массивы
4.8.3.1 Инициализаторы массивов могут быть блочными
Инициализатор массива может быть отформатирован как блочная конструкция. Например, все следующие варианты допустимы (список примеров не полный):
new int[] {           
    0, 1, 2, 3            
}                       
                        
new int[] {             
    0, 1,               
    2, 3
}               

new int[]
    {0, 1, 2, 3}     

new int[] {
    0,
    1,
    2,
    3
}
4.8.3.2 Не объявляйте массивы в стиле языка С
Квадратные скобки ставятся после типа, а не после переменной: String[] args, а не String args[].
4.8.4 Операторы и выражения switch
По историческим причинам в языке Java существует два различных синтаксиса для switch, которые мы можем называть старым и новым стилем. Новый стиль использует стрелку ( -> ) после меток switch, а старый стиль использует двоеточие ( : ).
Терминология
Внутри фигурных скобок блока switch находятся либо одно или несколько правил switch (новый стиль); либо одна или несколько групп операторов (старый стиль). Правило switch состоит из метки switch (case ... или default), за которой следует -> и выражение, блок или оператор throw. Группа операторов состоит из одной или нескольких меток switch, каждая из которых следует за двоеточием, затем одного или нескольких операторов или, для последней группы операторов, нуля или более операторов. (Эти определения соответствуют спецификации языка Java, §14.11.)
4.8.4.1 Отступ
Как и в случае с любым другим блоком, содержимое блока switch смещается на два пробела (здесь и далее лучше на четыре — примечание переводчика).
В новом стиле switch правило switch может быть записано в одной строке, если оно в остальном соответствует стилю Google. (Оно не должно превышать ограничение на длину строки, и если оно содержит непустой блок, то после { должен быть перенос строки.) Применяются правила переноса строк из раздела 4.5, включая отступ +4 для строк-продолжений. Для правила switch с непустым блоком после стрелки применяются те же правила, что и для блоков в других местах: строки между { и } имеют дополнительный отступ +2 относительно строки с меткой switch.
switch (number) {
    case 0, 1 -> handleZeroOrOne();
    case 2 ->
        handleTwoWithAnExtremelyLongMethodCallThatWouldNotFitOnTheSameLine();
    default -> {
        logger.atInfo().log("Surprising number %s", number);
        handleSurprisingNumber(number);
    }
}
В старом стиле switch двоеточие каждой метки switch следует за переносом строки. Операторы внутри группы операторов имеют дополнительный отступ в два пробела.
4.8.4.2 Сквозной проход комментируется
В старом стиле switch каждая группа операторов либо завершается явно (с помощью break, continue, return или выбрасывания исключения), либо помечается комментарием, указывающим, что выполнение будет или может продолжиться в следующей группе операторов. Достаточно любого комментария, передающего идею сквозного выполнения (обычно // fall through). Этот специальный комментарий не требуется в последней группе операторов блока switch. Пример:
switch (input) {
    case 1:
    case 2:
        prepareOneOrTwo();
        // fall through
    case 3:
        handleOneTwoOrThree();
        break;
    default:
        handleLargeNumber(input);
}
Обратите внимание, что комментарий ставится не после case 1, а лишь в конце группы операторов.
В новом стиле switch сквозного выполнения нет.
4.8.4.3 Полнота и наличие метки default
Язык Java требует, чтобы выражения switch и многие виды операторов switch были полными. Это эффективно означает, что каждое возможное значение, по которому может осуществляться переключение, будет соответствовать одной из меток switch. switch является полным, если в нём есть метка default, но также, например, если переключаемое значение является перечислением и каждое значение перечисления соответствует метке switch. Стиль Google требует, чтобы каждый switch был полным, даже те, где сам язык этого не требует. Это может потребовать добавления метки default, даже если она не содержит кода.
4.8.4.4 Выражения switch
Выражения switch должны использовать новый стиль:
return switch (list.size()) {
    case 0 -> "";
    case 1 -> list.getFirst();
    default -> String.join(", ", list);
};
4.8.5 Аннотации
4.8.5.1 Аннотации использования типа
Аннотации использования типа появляются непосредственно перед аннотированным типом. Аннотация является аннотацией использования типа, если она помечена метааннотацией @Target(ElementType.TYPE_USE). Пример:
final @Nullable String name;

public @Nullable Person getPersonByName(String name);
4.8.5.2 Аннотации классов, пакетов и модулей
Аннотации, применяемые к объявлению класса, пакета или модуля, указываются сразу после блока документации. Каждая аннотация размещается на отдельной строке (по одной аннотации на строку). Эти переносы строк не считаются переносом строки (см. Раздел 4.5), поэтому уровень отступа не увеличивается. Пример:
/** Класс. */
@Deprecated
@CheckReturnValue
public final class Frozzler { ... }

/** Пакет. */
@Deprecated
@CheckReturnValue
package com.example.frozzler;

/** Модуль. */
@Deprecated
@SuppressWarnings("CheckReturnValue")
module com.example.frozzler { ... }
4.8.5.3 Аннотации методов и конструкторов
Правила для аннотаций в объявлениях методов и конструкторов такие же, как в предыдущем разделе. Пример:
@Deprecated
@Override
public String getNameIfPresent() { ... }
Исключение: одиночная аннотация без параметров может указываться вместе с первой строкой сигнатуры.
Например:
@Override public int hashCode() { ... }
4.8.5.4 Аннотации полей
Аннотации, применяемые к полю, также указываются сразу после блока документации, но в этом случае несколько аннотаций (возможно, параметризованных) могут быть перечислены в одной строке.
@Partial @Mock DataLoader loader;
4.8.5.5 Аннотации параметров и локальных переменных
Нет конкретных правил для форматирования аннотаций параметров или локальных переменных (за исключением случаев, когда аннотация является аннотацией использования типа).
4.8.6 Комментарии
Этот раздел посвящен комментариям реализации. Javadoc рассматривается отдельно в Разделе 7.
Любому переносу строки может предшествовать произвольное количество пробелов, за которым следует комментарий реализации. Такой комментарий делает строку непустой.
4.8.6.1 Стиль блочных комментариев
Блочные комментарии имеют тот же отступ, что и окружающий код. Они могут быть в стиле /* ... */ или // ... . Для многострочных комментариев вида /* … */ последующие строки должны начинаться с символа *, выравненного по * в предыдущей строке.
/*
 * This is          // And so           /* Or you can
 * okay.            // is this.          * even do this. */
 */
Комментарии не заключаются в рамки, нарисованные звездочками или другими символами.
Совет: При написании многострочных комментариев используйте стиль /* … */, если хотите, чтобы средства автоматического форматирования кода делали перенос строки при необходимости (в стиле параграфов). Большинство средств форматирования не могут делать этого с блоками однострочных комментариев // …
4.8.6.2 Комментарии TODO
Используйте комментарии TODO для кода, который является временным, краткосрочным решением или достаточно хорошим, но не идеальным.
Комментарий TODO начинается со слова TODO заглавными буквами, затем двоеточие и ссылка на ресурс, содержащий контекст, в идеале ссылка на баг. Ссылка на баг предпочтительнее, потому что баги отслеживаются и имеют последующие комментарии. После этого контекста следует пояснительная строка, введённая через дефис -.
Цель — иметь единый формат TODO, который можно искать, чтобы узнать, как получить больше подробностей.
// TODO: crbug.com/12345678 - Удалить это после истечения окна совместимости 2047q4.
Избегайте добавления TODO, которые ссылаются на человека или команду как на контекст:
// TODO: @ваше_имя_пользователя - Создайте задачу и используйте '*' для повтора.
Если ваш TODO имеет форму «Сделать что-то в будущем», убедитесь, что вы указываете либо очень конкретную дату («Исправить к ноябрю 2005 года»), либо очень конкретное событие («Удалить этот код, когда все клиенты смогут обрабатывать XML-ответы»).
4.8.7 Модификаторы
Модификаторы классов и полей, если они присутствуют, отображаются в порядке, рекомендованном спецификацией языка Java:
public protected private abstract default static final sealed non-sealed transient volatile synchronized native strictfp
Модификаторы в директивах requires модуля, когда они присутствуют, указываются в следующем порядке:
transitive static
4.8.8 Числовые литералы
Целочисленные литералы типа long используют суффикс L в верхнем регистре, никогда в нижнем (чтобы избежать путаницы с цифрой 1). Например, 3000000000L, а не 3000000000l.
4.8.9 Текстовые блоки
Открывающие """ текстового блока всегда находятся на новой строке. Эта строка может либо следовать тем же правилам отступа, что и другие конструкции, либо вообще не иметь отступа (начинаться с левого поля). Закрывающие """ находятся на новой строке с тем же отступом, что и открывающие """, и могут следовать в той же строке за дополнительным кодом. Каждая строка текста в текстовом блоке имеет отступ не менее, чем открывающие и закрывающие """. (Если строка имеет больший отступ, то строковый литерал, определяемый текстовым блоком, будет содержать пробелы в начале этой строки.)

Содержимое текстового блока может превышать ограничение на длину строки.
Оригинал статьи Google Java Style Guide
Оцените статью, если она вам понравилась!