该规范参考Kotlin语言中文站进行定义
应用codeStyle
一、源代码组织结构
1、目录结构
Kotlin和Java相似,都有包的概念。每一个Kotlin文件都能以一条package语句开头,那么这个文件中的所有类、函数及属性都会被放到这个包中,属于这个包。
如果其它文件中也是以同样的package语句开头,那么它们属于同一个包,不管文件放在哪个目录结构下,不用和Java一样,必须把类文件放在和包结构相匹配的目录结构下。同一个包下的内容可以互相使用。
Kotlin推荐的目录结构遵循省略了公共根包的包结构,即每个文件应存储在与其 package 语句对应的目录中。
一句话概况:每个文件应存储在与其 package 语句对应的目录中!
2、源文件名称
如果 Kotlin 文件包含单个类(以及可能相关的顶层声明),那么文件名应该与该类的名称相同,并追加 .kt 扩展名。如果文件包含多个类或只包含顶层声明, 那么选择一个描述该文件所包含内容的名称,并以此命名该文件。 使用首字母大写的驼峰风格, 例如 ProcessDeclarations.kt
。
一句话概况:文件名采用驼峰命名
3、类布局
通常,一个类的内容按以下顺序排列:
属性声明与初始化块
次构造函数
方法声明
伴生对象
不要按字母顺序或者可见性对方法声明排序,也不要将常规方法与扩展方法分开。而是要把相关的东西放在一起,这样从上到下阅读类的人就能够跟进所发生事情的逻辑。
将嵌套类放在紧挨使用这些类的代码之后。如果打算在外部使用嵌套类,而且类中并没有引用这些类,那么把它们放到末尾,在伴生对象之后。
二、命名规则
在 Kotlin 中,包名与类名的命名规则非常简单:
包的名称总是小写且不使用下划线(org.example.project)。 通常不鼓励使用多个词的名称,但是如果确实需要使用多个词,可以将它们连接在一起或使用驼峰风格(org.example.myProject)。
类与对象的名称以大写字母开头并使用驼峰风格:
open class DeclarationProcessor { /*……*/ }
object EmptyDeclarationProcessor : DeclarationProcessor() { /*……*/ }
函数名
函数、属性与局部变量的名称以小写字母开头、使用驼峰风格而不使用下划线:
fun processDeclarations() { /*……*/ }
var declarationCount = 1
例外:用于创建类实例的工厂函数可以与抽象返回类型具有相同的名称:
interface Foo { /*……*/ }
class FooImpl : Foo { /*……*/ }
fun Foo(): Foo { return FooImpl() }
属性名
常量名称(标有 const
的属性,或者保存不可变数据的没有自定义 get
函数的顶层/对象 val
属性)应该使用大写、下划线分隔的名称:
const val MAX_COUNT = 8
val USER_NAME_FIELD = "UserName"
保存带有行为的对象或者可变数据的顶层/对象属性的名称应该使用驼峰风格名称:
val mutableCollection: MutableSet<String> = HashSet()
保存单例对象引用的属性的名称可以使用与 object 声明相同的命名风格:
val PersonComparator: Comparator<Person> = /*...*/
对于枚举常量,可以使用大写、下划线分隔的名称(enum class Color { RED, GREEN }
)也可使用首字母大写的常规驼峰名称,具体取决于用途。
幕后属性的名称
如果一个类有两个概念上相同的属性,一个是公共 API 的一部分,另一个是实现细节,那么使用下划线作为私有属性名称的前缀(这种方式借鉴了Flutter私有属性的命名方式):
class C {
private val _elementList = mutableListOf<Element>()
val elementList: List<Element>
get() = _elementList
}
命名有良好习惯
类的名称通常是用来解释类是什么的名词或者名词短语:xxxListAdapter、 PersonReader。
方法的名称通常是动词或动词短语,说明该方法做什么:close、 readPersons。 修改对象或者返回一个新对象的名称也应遵循建议。例如 sort 是对一个集合就地排序,而 sorted 是返回一个排序后的集合副本。
名称应该表明实体的目的是什么,所以最好避免在名称中使用无意义的单词 。
当使用首字母缩写作为名称的一部分时,如果缩写由两个字母组成,就将其大写(IOStream); 而如果缩写更长一些,就只大写其首字母(XmlFormatter、 HttpInputStream)。
格式化
使用 4 个空格缩进。不要使用 tab。(可在AS中设置好code style,一个tab等于4个空格进行缩进)
对于花括号,将左花括号放在结构起始处的行尾,而将右花括号放在与左括结构横向对齐的单独一行。
if (elements != null) {
for (element in elements) {
// ……
}
}
在 Kotlin 中,分号是可选的,因此换行很重要。语言设计采用 Java 风格的花括号格式,如果尝试使用不同的格式化风格,那么可能会遇到意外的行为。
横向空白
在二元操作符左右留空格(a + b)。例外:不要在“range to”操作符(0..i)左右留空格。
不要在一元运算符左右留空格(a++)
在控制流关键字(if、 when、 for 以及 while)与相应的左括号之间留空格。
(这里容易被忽略,小组讨论是否需要。建议使用)
不要在主构造函数声明、方法声明或者方法调用的左括号之前留空格。
class A(val x: Int)
fun foo(x: Int) { …… }
fun bar() {
foo(1)
}
绝不在 (、 [ 之后或者 ]、 ) 之前留空格。
绝不在. 或者 ?. 左右留空格:foo.bar().filter { it > 2 }.joinToString(), foo?.bar()
在 // 之后留一个空格:
// 这是一条注释
不要在用于指定类型参数的尖括号前后留空格:class Map<K, V> { …… }
不要在 :: 前后留空格:Foo::class、 String::length
不要在用于标记可空类型的 ? 前留空格:String?
冒号
在以下场景中的冒号":"之前留一个空格:
当它用于分隔类型与超类型时;
当委托给一个超类的构造函数或者同一类的另一个构造函数时;
在 object 关键字之后。
而当分隔声明与其类型时,不要在 : 之前留空格。(指声明属性时或者函数定义返回类型时)
在 : 之后总要留一个空格。
abstract class Foo<out T : Any> : IFoo {
abstract fun foo(a: Int): T
}
class FooImpl : Foo() {
constructor(x: String) : this(x) { /*……*/ }
val x = object : IFoo { /*……*/ }
}
类头格式化
有少数几个参数的类可以写成一行:
class Person(id: Int, name: String)
具有较长类头的类应该格式化,以使每个主构造函数参数位于带有缩进的单独一行中。 此外,右括号应该另起一行。如果我们使用继承,那么超类构造函数调用或者实现接口列表应位于与括号相同的行上:
class Person(
id: Int,
name: String,
surname: String
) : Human(id, name) {
// ……
}
对于多个接口,应首先放置超类构造函数调用,然后每个接口应位于不同的行中:
class Person(
id: Int,
name: String,
surname: String
) : Human(id, name),
KotlinMaker {
// ……
}
对于具有很长超类型列表的类,在冒号后面换行,并横向对齐所有超类型名:
class MyFavouriteVeryLongClassHolder :
MyLongHolder<MyFavouriteVeryLongClass>(),
SomeOtherInterface,
AndAnotherOne {
fun foo() { /*...*/ }
}
为了将类头与类体分隔清楚,当类头很长时,可以在类头后放一空行 (如上例所示)或者将左花括号放在独立行上:
class MyFavouriteVeryLongClassHolder :
MyLongHolder<MyFavouriteVeryLongClass>(),
SomeOtherInterface,
AndAnotherOne
{
fun foo() { /*...*/ }
}
个人较倾向于第一种:花括号放在超类后
构造函数参数使用常规缩进(4 个空格)。
理由:这确保了在主构造函数中声明的属性与 在类体中声明的属性具有相同的缩进。
Unit
如果函数返回 Unit 类型,该返回类型应该省略:
fun foo() { // 省略了 ": Unit"
}
Lambda表达式
在lambda表达式中, 大括号左右要加空格,分隔参数与代码体的箭头左右也要加空格 。lambda表达应尽可能不要写在圆括号中
list.filter { it > 10 }.map { element -> element * 2 }
在非嵌套的短lambda表达式中,最好使用约定俗成的默认参数 it 来替代显式声明参数名 。在嵌套的有参数的lambda表达式中,参数应该总是显式声明。
在多行的 lambda 表达式中声明参数名时,将参数名放在第一行,后跟箭头与换行符:
appendCommaSeparated(properties) { prop ->
val propertyValue = prop.get(obj) // ……
}
如果参数列表太长而无法放在一行上,请将箭头放在单独一行:
foo {
context: Context,
environment: Env
->
context.configureEnv(environment)
}