Google 官方 Kotlin 编码风格翻译
源文件
所有源文件编码必须是 UTF-8
。
命名
如果源文件只包含一个顶级类(Top-level),文件名应该命名为大写小敏感和 .kt
拓展名。其他情况如果源文件包含多个顶级声明,则选择一个描述文件内容的名称,使用驼峰命名法,并附上名称和 .kt
拓展名。
特殊编码
空格
除了换行,它是 ASCII 水平方向字符 (0x20) 唯一能出现在源文件中的空格。
这意味着:
- 所有其他空格字符在字符串和字符源文本中均被转义
- 制表符不用于缩进
特殊的转义序列
对于任何具有特殊转义的字符(b、n、r、t、’、 和 $)使用的是对应的 Unicode
编码(例如:Line Feed : n = u000a)转义。
Non-ASCII 字符
对于剩下的 Non-ASCII
字符,要么使用实际的 Unicode
字符(例如:∞),要么使用等效的 Unicode
转义(如:u221e)。这个选择只取决于代码易读和理解。对于任何位置的可打印的字符,都不建议使用 Unicode
转义,尤其在字符串源文本和注释。
val unitAbbrev = "μs" | 最好的:即使没有评论,也非常清楚 |
val unitAbbrev = "u03bcs" // μs" | 较差的:没有理由在一个可打印字符字符中使用转义字符表示 |
val unitAbbrev = "u03bcs" | 较差的:代码阅读者不知道这是一个什么概念 |
return "ufeff" + content | 良好的:对不可打印字符使用转义,必要时可以进行注释。 |
结构
一个 .kt
文件由以下组成:
- 版权和许可证(可选)
- File-Level 注解
- 包名声明
- 导包声明
- Top-level 声明
每个部分均用换行来分隔。
版权 / 许可证
如果文件中包含版权或许可证,则应将其放在多行注释的最上面。
不要使用 KDoc-style 或者单行注释。
File-Level 注解
如果使用 kotlin
的 File-Level
注解请放在头部和包名声明之间。
包名声明
包名声明不受行列限制并且 使用 line-wrap
模式。
导包声明
类、函数和属性的导入语句组合在一个单独列表中,并按 ASCII
排序。
不允许使用通配符导入。
和”包名声明”一样,导包声明不受行列限制并且 使用 line-wrap
模式。
Top-level 声明
一个 .kt
文件可以在顶层声明一个或多个类型、函数、属性或类型别名。
文件中内容应该围绕一个主题。
比如只有一个公开类型(public)或一组拓展函数,对于多个接收者都应该执行同样的操作。
比如(AreaMaths.kt
):
不相关的声明应该分离开来放到自己的文件中,并且在一个文件内公开声明应该最小化尽可能的保持内聚。
没有文件内容的数量和排序限制。
源文件一般是从上至下阅读,所以尽可能按这个顺序反应出相关重要的内容。因此不同的文件可能会选择不同的排序。比如说,一个文件可能包含 100 个属性,10 个函数,1 个类。最重要的一点,每个类应该以某种逻辑去排序它的成员,维护者应该要能解释这种排序逻辑。再如:新函数不应该添加在类的末尾,因为这样是日期排序,这不是一个逻辑排序。
成员变量排序
类成员变量的排序和 Top-level 声明一致
格式
大括号
when
分支和不具有 if/else
的分支且适用于单行的语句不需要大括号。
其他情况都需要大括号,比如说 if
、for
、when
、do
和 while
。即使语句体是空语句或者只有一句也需要大括号。
非空块
对于非空块和块状结构,大括号遵循 Kernighan(K) 和 Ritchie(R) 风格 (Egyptian brackets):
- 左大括号前不换行
- 左大括号后换行
- 右大括号前换行
- 如果右大括号是一个语句、函数体或类的终止,则右大括号后换行; 否则不换行。例如,如果右大括号后面是 else 或逗号,则不换行。
后续给出枚举类的一些例外。
空块
空语块或类似空语块的必须是 K&R 风格
表达式
只有在单行能够完成整个表达式的 if/else
语句才适用省略大括号。
缩进
每当开始一个新的块,缩进增加 4 个空格。
每行一个语句
每个语句后面都有一个换行符。不使用分号。
自动换行
一般情况下,一行长代码为了避免超出列限制(100个字符)而被分为多行,我们称之为自动换行(line-wrapping)。除了以下指出的情况,任何超出这些限制的必须按照说明换行。
- 不可能遵循列限制的行(例如,KDoc中的一个长URL)
- 包和导入语句
- 在注释中可以剪切并粘贴到 shell 中的命令行。
从哪里断开
自动换行的基本准则是:更倾向于在更高的语法级别处断开。
- 如果在非赋值运算符处断开,那么在该符号前断开( +,它将位于下一行)。这条规则也适用以下类操作符语法:
- 点分隔符(.)
- 双冒号(::)
- 当赋值运算符在一行断开,符号后面会出现断开。
- 一个方法或者构造函数的参数名依然在左括号后面
- 逗号(,)与其前面的内容留在同一行。
- lambda 的 -> 符号和参数列表在同一行
继续缩进
当自动换行时,第一行后的每一行至少比第一行多缩进 4 个空格(注意:制表符不用于缩进)。当存在连续自动换行时,缩进可能会多缩进不止 4 个空格(语法元素存在多级时)。一般而言,两个连续行使用相同的缩进当且仅当它们开始于同级语法元素。
函数
当一个函数签名不适合一行时,将每个参数声明分解到它自己的行上。在这种格式中定义的参数应该使用继续缩进(+8)。闭括号()
)和返回类型被放在它们自己的行上,没有附加的缩进。
表达式函数
当一个函数只包含一个表达式时,它可以被表示为一个表达式函数。
替换为
表达式函数不应该使用换行导致用两行表示。
如果表达式函数需要拓展则需要换行,使用正常的函数体、返回声明和正常的表达式换行规则。
属性
当一个属性初始化器不适用一行时,在等号(=)之后之换行并使用继续缩进规则。
属性声明一个 getter 或者 setter 函数应该在它们所在行前加一个普通缩进(+4)。
使用与函数相同规则进行格式化。
对于只读属性可以使用更简洁的语法,适用于一行。
空白
垂直方向
垂直方向空白行出现原则:
- 类的连续成员: 属性、构造函数、函数、嵌套类等等。
- 例外:
- 两个连续属性之间的空行(在它们之间没有其他的代码)是可选的。
- 如果存在,则使用这些空白行来创建属性的逻辑分组,并将属性与它们的相关属性关联起来。
- 在函数体内,语句的逻辑分组间使用空行。
- 类内的第一个成员变量前或最后一个成员变量后的空行是可选的(既不鼓励也不反对这样做,视个人喜好而定)。
- 根据本文档的其他部分(如“结构”一节)的要求。
允许使用多个连续的空行,但不鼓励或不需要。
水平方向
除了语言需求和其它规则,并且除了文字,注释和 Kdoc 用到单个空格,单个 ASCII 空格也可以出现在以下几个地方:
- 分隔任何保留字与紧随其后的左括号(
(
)(如:if、for 和 catch)。
- 分隔任何保留字与其前面的右大括号(
}
) (如:else, catch)。
- 任何左大括号(
{
)前
- 在任何二元或三元运算符的两侧
同样使用于 “类似操作符” 语法:
- lambda 表达式(
->
)
但是不适用于:
- 双冒号(
::
)成员引用语法
- 逗号(
.
)
- 在一个冒号前使用(
:
)空格,仅在类使用基类或接口,或者用于where
泛型约束时候。
- 逗号(
,
)或冒号(:
)后。
- 如果在一条语句后做注释,则双斜杠(//)两边都要空格。这里可以允许多个空格,但没有必要。
特殊结构
枚举类
一个没有函数的枚举,且它的常量没有文档,可以随意地格式化为单行。
当枚举中的常量被放在单独的行上时,它们之间不需要空白行,除非它们定义了一个正体。
因为 enum 类是类,所以所有其他用于格式化类的规则都适用。
注解
在构造注释之前,成员或类型注释被放在单独的行上。
没有参数的注释可以放在一行上。
当只有一个没有参数的注释时,可以放在一行上。
隐式返回/属性类型
如果表达式函数体或属性初始化器是标量值,或者返回类型可以从正文中清楚地推断出来,那么就可以省略它。
在编写库时,当它是公共 API
的一部分时,保留显式类型声明,方便框架使用者调用。
命名
识符只能使用 ASCII 字母和数字,因此每个有效的标识符名称都能匹配正则表达式 w+
。
在 Google
其它编程语言风格中使用的特殊前缀或后缀,如name_
, mName
, s_name
和 kName
,在 Java
编程风格中都不再使用。
特殊的前缀或后缀,如示例中所见的:name_
,mName
、s_name
和 kName
,除了在备份属性(参见“支持属性”),这些都一律不使用。
包名
包名都是小写的,连续的单词简单地连接在一起(没有下划线)。
类名
类名是用使用驼峰命名,通常是名词或名词短语。例如:Character
或 ImmutableList
。接口名也可能是名词或名词短语(例如:List
),但我的有时是形容词或形容词短语(例如:Readable
)。
函数名
函数名用使用驼峰命名,通常是动词或动词短语。例如:sendMessage
或 stop
。下划线允许出现在测试函数名中,以分离名称的逻辑组件。
常量命名
常量名命名模式为 CONSTANT_CASE
,全部字母大写,用下划线分隔单词。那,到底什么算是一个常量?
常量是使用 val
定义并且没有 getter,其内容是不可更改的,其函数没有副作用。这包括不可变类型和不可变集合的不可变类型,如果它们标记为 const 则和标量和字符串一样。
这些名字通常是名词或名词短语。常量值只能在一个 object class
定义或 top-level
中定义。否则,在类内部定义一个常量但定义的值必须使用一个非常量名。标量值的常量必须使用 const
修饰符。
非常量命名
非常量的名称使用骆驼拼写法。应用于实例属性、本地属性和参数名。
这些名字通常是名词或名词短语。
备份属性
当需要一个支持属性时,它的名字应该与真正的属性完全匹配,区别在于有一个下划线。
类型变量名
类型变量可用以下两种风格之一进行命名:
- 单个的大写字母,后面可以跟一个数字(如:
E
,T
,X
,T2
)。 - 以类命名方式,后面加个大写的T(如:
RequestT
,FooBarT
)。
驼峰式命名法(CamelCase)
驼峰式命名法分大驼峰式命名法(UpperCamelCase)和小驼峰式命名法(lowerCamelCase)。 有时,我们有不只一种合理的方式将一个英语词组转换成驼峰形式,如缩略语或不寻常的结构(例如”IPv6”或”iOS”)。Google指定了以下的转换方案。
名字从散文形式(prose form)开始:
- 把短语转换为纯ASCII码,并且移除任何单引号。例如:”Müller’s algorithm”将变成”Muellers algorithm”。
- 把这个结果切分成单词,在空格或其它标点符号(通常是连字符)处分割开。
- 推荐:如果某个单词已经有了常用的驼峰表示形式,按它的组成将它分割开(如”AdWords”将分割成”ad words”)。 需要注意的是”iOS”并不是一个真正的驼峰表示形式,因此该推荐对它并不适用。
- 现在将所有字母都小写(包括缩写),然后将单词的第一个字母大写:
- 每个单词的第一个字母都大写,来得到大驼峰式命名。
- 除了第一个单词,每个单词的第一个字母都大写,来得到小驼峰式命名。
- 最后将所有的单词连接起来得到一个标识符。
示例:
“XML HTTP request” | XmlHttpRequest | XMLHTTPRequest |
“new customer ID” | newCustomerId | newCustomerID |
“inner stopwatch” | innerStopwatch | innerStopWatch |
“supports IPv6 on iOS?” | supportsIpv6OnIos | supportsIPv6OnIOS |
“YouTube importer” | YouTubeImporter or YoutubeImporter* |
加星号处表示可以,但不推荐。
文档
文档格式
KDoc 块的基本格式如下所示:
单行注释例子:
基本形式是可以接受的。
当整个 KDoc 块(包括注释标记)可以放在一行上时,可以替换单行表单。
请注意,只有在没有诸如 @return 这样的块标记时才会这样做。
段落
空行(即:只包含最左侧星号的行) - 出现在段落和段落标记组之前的标签。
块标签
标准 KDoc 出现顺序 @constructor
, @receiver
, @param
, @property
, @return
, @throws
, @see
,出现这些都不会出现在空的描述。
当一个块标记不适合一行时,下一行从 @ 的位置缩进 8 个空格。
摘要片段
每个类或成员的 KDoc 以一个简短的摘要片段开始。这个片段是非常重要的,在某些情况下,它是唯一出现的文本,比如在类和方法索引中。
这只是一个小片段,可以是一个名词短语或动词短语,但不是一个完整的句子。它不会以 “A Foo is a…” 或 “This method returns…” 开头, 它也不会是一个完整的祈使句,如 “Save the record…”。然而,由于开头大写及被加了标点,它看起来就像是个完整的句子。
哪里需要使用 KDoc
至少在每个 public 类及它的每个 public 和 protected 成员变量处使用 KDoc,以下是一些例外:
- 不言自明的方法,对于简单明显的方法如 getFoo,KDoc是可选的(即:是可以不写的)。这种情况下除了写 “Returns the foo”,确实也没有什么值得写了。
- 如果有一些相关信息是需要读者了解的,那么以上的例外不应作为忽视这些信息的理由。例如,对于方法名 getCanonicalName,就不应该忽视文档说明,因为读者很可能不知道词语 canonical name 指的是什么。
- 如果一个方法重写了超类中的方法,那么 KDoc 并非必需的。