Skip to Content
Java4 格式化

4 格式化

**术语说明:**块状结构(block-like construct)指类、方法、构造函数或 switch 的主体。注意,根据第 4.8.3.1 节关于数组初始化器的规定,任何数组初始化器都可以选择性地被视为块状结构。

4.1 花括号

4.1.1 可选花括号的使用

ifelsefordowhile 语句中使用花括号,即使主体为空或只包含一条语句。

其他可选花括号(例如 lambda 表达式中的花括号)仍然是可选的。

4.1.2 非空块:K & R 风格

对于非空块和块状结构,花括号遵循 Kernighan 和 Ritchie 风格:

  • 左花括号前不换行,下文详述的情况除外。
  • 左花括号后换行。
  • 右花括号前换行。
  • 右花括号后换行,仅当该花括号终结一条语句或终结方法、构造函数或命名类的主体时。例如,如果花括号后面跟着 else 或逗号,则花括号后换行。

例外:在这些规则允许以分号(;)结尾的单条语句的位置,可以出现一个语句块,该块的左花括号前需要换行。此类块通常用于限制局部变量的作用域。

示例:

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/elsetry/catch/finally)的一部分。

示例:

// 这是可以接受的 void doNothing() {} // 这同样可以接受 void doNothingElse() { }
// 这是不可接受的:多块语句中不允许使用简洁空块 try { doSomething(); } catch (Exception e) {}

4.2 块缩进:+2 个空格

每当打开一个新的块或块状结构时,缩进增加两个空格。当块结束时,缩进恢复到之前的级别。缩进级别适用于块中的代码和注释。(参见第 4.1.2 节的示例,非空块:K & R 风格。)

4.3 每行一条语句

每条语句后面都跟一个换行符。

4.4 列限制:100

Java 代码的列限制为 100 个字符。这里的”字符”指任何 Unicode 码点。除下文另有说明外,任何超过此限制的行都必须进行换行,如第 4.5 节换行所述。

每个 Unicode 码点计为一个字符,即使其显示宽度大于或小于一个字符。例如,如果使用全角字符 ,你可以选择在此规则严格要求的位置之前换行。

例外:

  1. 无法遵守列限制的行(例如,Javadoc 中的长 URL,或长的 JSNI 方法引用)。
  2. package 声明和导入语句(参见第 3.2 节包声明和第 3.3 节导入语句)。
  3. 文本块的内容。
  4. 注释中可能被复制粘贴到 shell 中的命令行。
  5. 非常长的标识符,在极少数需要使用的场合,允许超过列限制。在这种情况下,周围代码的有效换行方式由 google-java-format  生成。

4.5 换行

**术语说明:**当原本可以占用一行的代码被分成多行时,这种行为称为换行(line-wrapping)。

不存在一个全面的、确定性的公式来指导每种情况下的换行方式。很多时候,对于同一段代码存在多种有效的换行方式。

**注意:**虽然换行的典型原因是避免超出列限制,但即使实际上能放在列限制内的代码,作者也可以自行决定换行。

**提示:**提取方法或局部变量可以在不需要换行的情况下解决问题。

4.5.1 在哪里断行

换行的首要原则是:优先在更高的语法层级处断行。此外:

  1. 当在非赋值运算符处断行时,断行位于该符号之前。(注意,这与 Google 风格中其他语言(如 C++ 和 JavaScript)的做法不同。)

    • 这也适用于以下”类运算符”符号:
      • 点分隔符(.
      • 方法引用的双冒号(::
      • 类型限定中的 & 符号(<T extends Foo & Bar>
      • catch 块中的管道符(catch (FooException | BarException e))。
  2. 当在赋值运算符处断行时,通常断行位于该符号之后,但两种方式都可以接受。

    • 这也适用于增强 for(“foreach”)语句中的冒号。
  3. 方法名、构造函数名或记录类名紧跟其后的左括号(()。

  4. 逗号(,)紧跟在其前面的标记之后。

  5. 不在 lambda 或 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. 将任何关键字(如 ifforcatch)与该行上紧随其后的左括号(()分隔

  2. 将任何关键字(如 elsecatch)与该行上其前面的右花括号(})分隔

  3. 在任何左花括号({)之前,有两个例外:

    • @SomeAnnotation({a, b})(不使用空格)
    • String[][] x = {{"foo"}};(根据下面第 10 条,{{ 之间不需要空格)
  4. 在任何二元或三元运算符的两侧。这也适用于以下”类运算符”符号:

    • 分隔多个类型限定的 & 符号:<T extends Foo & Bar>
    • 处理多个异常的 catch 块中的管道符:catch (FooException | BarException e)
    • 增强 for(“foreach”)语句中的冒号(:
    • lambda 表达式中的箭头:(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 水平对齐:从不要求

**术语说明:**水平对齐(Horizontal alignment)是在代码中添加数量不定的额外空格,以使某些标记直接出现在前几行的某些其他标记下方的做法。

这种做法是允许的,但 Google 风格从不要求这样做。甚至不要求在已经使用水平对齐的地方保持水平对齐。

以下是不使用对齐和使用对齐的示例:

private int x; // this is fine private Color color; // this too private int x; // permitted, but future edits private Color color; // may leave it unaligned

提示:对齐有助于提高可读性,但为了保持对齐而进行的维护会在未来造成问题。例如,考虑一个只涉及一行的更改。如果该更改破坏了之前的对齐,那么重要的是不要仅仅为了重新对齐而在附近的行上引入额外的更改。在原本不受影响的行上引入格式更改会破坏版本历史,减慢审查速度,并加剧合并冲突。这些实际问题优先于对齐。

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 循环的头部中可以接受多个变量声明。

4.8.2.2 需要时才声明

局部变量习惯性地在其所在块或块状结构的开头声明。相反,局部变量在接近首次使用的位置声明(在合理范围内),以最小化其作用域。局部变量声明通常带有初始化器,或在声明后立即初始化。

4.8.3 数组

4.8.3.1 数组初始化器:可以是”块状的”

任何数组初始化器都可以选择像”块状结构”一样格式化。例如,以下写法都是有效的(不是详尽列表):

new int[] { new int[] { 0, 1, 2, 3 0, } 1, 2, new int[] { 3, 0, 1, } 2, 3 } new int[] {0, 1, 2, 3}
4.8.3.2 不使用 C 风格的数组声明

方括号是类型的一部分,而不是变量的一部分:应写 String[] args,而不是 String args[]

4.8.4 Switch 语句和表达式

由于历史原因,Java 语言有两种不同的 switch 语法,我们可以称之为旧式新式。新式 switch 在 switch 标签后使用箭头(->),而旧式 switch 使用冒号(:)。

**术语说明:**在 switch 块的花括号内,要么是一个或多个 switch 规则(新式);要么是一个或多个语句组(旧式)。switch 规则由一个 switch 标签case ...default)后跟 -> 和一个表达式、块或 throw 组成。语句组由一个或多个 switch 标签(每个后跟冒号)组成,然后是一条或多条语句;对于最后一个语句组,可以是零条或多条语句。(这些定义与 Java 语言规范 §14.11  一致。)

4.8.4.1 缩进

与任何其他块一样,switch 块的内容缩进 +2。每个 switch 标签从此 +2 缩进开始。

在新式 switch 中,如果 switch 规则符合 Google 风格,可以写在一行上。(它不能超过列限制,且如果包含非空块,则 { 之后必须换行。)第 4.5 节的换行规则适用,包括续行的 +4 缩进。对于箭头后带非空块的 switch 规则,与其他块相同的规则适用:{} 之间的行相对于 switch 标签所在行再缩进 +2。

switch (number) { case 0, 1 -> handleZeroOrOne(); case 2 -> handleTwoWithAnExtremelyLongMethodCallThatWouldNotFitOnTheSameLine(); default -> { logger.atInfo().log("Surprising number %s", number); handleSurprisingNumber(number); } }

在旧式 switch 中,每个 switch 标签的冒号后面跟一个换行符。语句组内的语句再缩进 +2。

4.8.4.2 Fall-through:需要注释

在旧式 switch 块中,每个语句组要么以中断方式终止(使用 breakcontinuereturn 或抛出异常),要么用注释标明执行会或可能继续到下一个语句组。任何表达 fall-through 意图的注释都是足够的(通常是 // fall through)。在 switch 块的最后一个语句组中不需要此特殊注释。示例:

switch (input) { case 1: case 2: prepareOneOrTwo(); // fall through case 3: handleOneTwoOrThree(); break; default: handleLargeNumber(input); }

注意 case 1: 之后不需要注释,只需在语句组的末尾添加。

新式 switch 中不存在 fall-through。

4.8.4.3 穷尽性和 default 标签的存在

Java 语言要求 switch 表达式和许多类型的 switch 语句是穷尽的(exhaustive)。这实际上意味着被 switch 的每个可能值都会被某个 switch 标签匹配。如果 switch 有 default 标签则是穷尽的,但例如当被 switch 的值是枚举且每个枚举值都被 switch 标签匹配时也是穷尽的。Google 风格要求每个 switch 都是穷尽的,即使语言本身不要求也是如此。这可能需要添加 default 标签,即使它不包含任何代码。

4.8.4.4 Switch 表达式

Switch 表达式必须使用新式 switch:

return switch (list.size()) { case 0 -> ""; case 1 -> list.getFirst(); default -> String.join(", ", list); };

4.8.5 注解

4.8.5.1 类型使用注解

类型使用注解(Type-use annotation)出现在被注解的类型之前。如果一个注解使用 @Target(ElementType.TYPE_USE) 进行元注解,则它是类型使用注解。示例:

final @Nullable String name; public @Nullable Person getPersonByName(String name);
4.8.5.2 类、包和模块注解

应用于类、包或模块声明的注解出现在文档块之后,每个注解独占一行(即每行一个注解)。这些换行不构成换行(第 4.5 节,换行),因此缩进级别不增加。示例:

/** This is a class. */ @Deprecated @CheckReturnValue public final class Frozzler { ... }
/** This is a package. */ @Deprecated @CheckReturnValue package com.example.frozzler;
/** This is a module. */ @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 节Javadoc 中单独讨论。

任何换行之前都可以有任意空白后跟一个实现注释。这种注释使该行变为非空行。

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 开头,后跟一个冒号和一个包含上下文的资源链接,最好是 bug 引用。Bug 引用更好,因为 bug 可以被跟踪且有后续评论。在此上下文之后,用连字符 - 引入解释性字符串。

目的是建立一致的 TODO 格式,以便通过搜索找到获取更多详情的方法。

// TODO: crbug.com/12345678 - Remove this after the 2047q4 compatibility window expires.

避免添加以个人或团队为上下文的 TODO:

// TODO: @yourusername - File an issue and use a '*' for repetition.

如果你的 TODO 是”将来某个时候做某事”的形式,请确保包含一个非常具体的日期(“2005 年 11 月前修复”)或一个非常具体的事件(“当所有客户端都能处理 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 文本块

文本块的开头 """ 始终在新行上。该行可以遵循与其他结构相同的缩进规则,也可以完全没有缩进(即从左边距开始)。结尾的 """ 在新行上,与开头的 """ 保持相同的缩进,并且可以在同一行上跟随后续代码。文本块中的每行文本至少与开头和结尾的 """ 缩进相同。(如果某行缩进更多,则文本块定义的字符串字面量在该行开头会有空格。)

文本块的内容可以超过列限制

Last updated on