前言:
本篇内容介绍Kotlin语言的编码风格,让我们在编写Kotlin的时候代码书写更规范。
方便其他阅读代码。
编码规范约束
如果需要根据风格指南配置Idea格式化程序。
在Idea之中,File ->Settings->Editor -> Code Style -> Kotlin 。
点击右上角的Set from... 并从菜单中选择Kotlin style guide.
如果需验证代码已经按照风格指南格式化,
File ->Settings->Editor->Inspections ->Kotlin-Style issues->File is not formatted according to project settings 在这个探查项后面打钩。 验证风格指南中描述的其他问题(例如各种编码约束)的附加探查项默认已启用。
源代码组织
目录结构
在纯粹的Kotlin项目中,推荐的目录结构遵循省略了公共根包的包结构。
对于JVM平台来说,Kotlin源文件(kt文件)
应当与java源文件位于同一个源文件根目录下。并且遵循相同的目录结构。每个文件应该存储在于其package 语句对应的目录中。
源文件名称
如果Kotlin文件包含单个类(以及可能相关的顶层声明),那么文件名应该与该类的名称相同,并追加.kt
扩展名。 我们如果使用IDEA创建文件这些都默认帮我们创建好了
源代码文件中的名称命名规范应该遵循驼峰命名方式。首字母大写
同时,应该避免在文件名中使用诸如Util
之类的无意义词语
源文件组织
鼓励多个声明(类,顶级函数,或者属性)放在同一个Kotlin源文件中。只要这些声明在语义上彼此紧密关联。并且文件保持在合理大小(代码行数最大几百行)。
针对为类定义与类的所有客户都相关的扩展函数时,请将它们放在于类自身定义相同的地方。而在定义仅对指定客户有意义的扩展函数时,请将它们放在紧挨该客户代码之后。不要为了保存Foo的所有扩展函数,而创建新的kt文件。
类布局
通常,一个类的内部内容按照以下顺序排列:
- 属性声明与初始化块
- 次构造器
- 方法声明
- 伴生对象
不要按照字母顺序或者可见性对方法声明排序,也不要将常规方法与扩展方法分开。
而是要将相关的东西放在一起。这样从上到下阅读类的人就能够跟进所发生事情的逻辑。选择一个顺序(高级别优先,或者相反)并一直坚持下去。
将嵌套类放在紧挨使用这些类的代码之后。
如果打算在外部使用嵌套类,而类中并没有引用这些类,那么将它们放在末尾,在伴生对象之后。
接口实现布局
在实现一个接口时,实现函数的顺序应该与该接口的成员顺序相同。
重载布局
在类中,将重载放在一起。
命名规范
在kotlin中,包名和类名的命名规范非常简单:
- 包的名称总是小写,并且不要使用下划线。不鼓励使用多个词的名称,但是如果确实需要使用多个词,可以将它们连接在一起使用驼峰风格(例如:org.example.myProject)。
- 类与对象的名称以大写字母开头,并使用驼峰风格。
函数名(方法名)
函数,属性与局部变量的名称以小写字母开头,并使用驼峰风格。不要使用下划线
测试方法的命名
在测试中,可以使用反引号括起来的带空格的方法名(Android运行时可能有问题,不支持该方法名),也可以使用下划线。
实例:
class MyTestCase {
@Test fun `ensure everything works`() { /*...*/ }
@Test fun ensureEverythingWorks_onAndroid() { /*...*/ }
}
属性名
常量名称(val,const
)应该使用大写,下划线分割命令名称。
保存带有行为的对象或者可变数据的顶层/对象属性的名称应该使用驼峰风格。
保存单例对象引用的属性的名称可以使用与Object
声明相同的命名风格。
对于枚举常量,使用大写,下划线分割的名称,也可以使用首字母大写的常规驼峰名称。
幕后属性的名称
如果一个类有两个概念上相同的属性,一个是公共API的一部分,一个是实现细节。
那么使用下划线作为私有属性的前缀:
实例:
class A {
private val _elementList = mutableListOf<Element>()
val elementList: List<Element>
get() = _elementList
}
总结
类的名称通常用来介绍类是用来干什么的名词或者名词短语: List,PersonReader
方法的名称通常是动词或者动词短语,说明该方法做什么: close,readPersons
。
修改对象或者返回一个新对象的名称也应该遵循建议。
名称应该表明实体的目的,尽量避免在名称中使用无意义的单词:Manager,Wrapper
等
(PS:所以说,命名规范啥的,了解了解就行了。真的完全遵循那是不可能的)
格式化
使用四个空格缩进,而不要使用tab进行缩进。
对于花括号,将花括号放在结构起始处的行尾,而将右花括号放在于左括号结构横向对齐的单独一行。
var elements: String? = null
if (elements != null) {
}
在kotlin之中,分号是可选的可以省略。
空格使用
不要在一元运算符左右留空格
在控制流关键字 if,when,for,while
相应的左括号之间留空格。
不要在.
,?.
左右留空格
不要在用于指定类型参数的尖括号前后留空格。
不要在 ::
前后留空格
不要在?
前面留空格
在:
的后面留一个空格。
类头格式化
具有少数主构造函数参数的类可以写一行。
多的换行。
修饰符
如果一个声明有多个修饰符,请按照以下顺序安放修饰符:
public / protected / private / internal
expect / actual
final / open / abstract / sealed / const
external
override
lateinit
tailrec
vararg
suspend
inner
enum / annotation / fun // 在 `fun interface` 中是修饰符
companion
inline
infix
operator
data
控制流语句
如果 if
或 when
语句的条件有多行,那么在语句体外边总是使用大括号。 将该条件的每个后续行相对于条件语句起始处缩进 4 个空格。 将该条件的右圆括号与左花括号放在单独一行:
在 when
语句中,如果一个分支不止一行,可以考虑用空行将其与相邻的分支块分开:
实例:
private fun parsePropertyValue(propName: String, token: Token) {
when (token) {
is Token.ValueToken ->
callback.visitValue(propName, token.value)
Token.LBRACE -> { // ……
}
}
}
如果是一个短分支,那么在条件相同的行上,可以省略花括号。
when (foo) {
true -> bar() // 好的写法
false -> { baz() } // 不好的写法
}
有别于java 的规范,并不是括号越多越好。
链式调用换行
当对链式调用换行时,将.
字符或者?.
操作符放在下一行
Lambda 表达式
在 lambda 表达式中,应该在花括号左右以及分隔参数与代码体的箭头左右留空格。 如果一个调用接受单个 lambda 表达式,应该尽可能将其放在圆括号外边传入。
在多行的 lambda 表达式中声明参数名时,将参数名放在第一行,后跟箭头与换行符
如果参数列表太长而无法放在一行上,请将箭头放在单独一行。
注释
对于较长的文档注释,将开头 /**
放在一个独立行中,并且后续每行都以星号开头:
Kotlin之中不建议在注释之中实习@param
与@return
避免重复结构
在Kotlin中语法结构提示需要省略的元素,建议去掉。不要在代码中保留不必要的语法元素。
例如:
fun foo(){ //返回值是unit 这里就省略了 :Unit
}
语言特性的惯用法
不可变性
优先使用不可变数据。初始化后未修改的局部变量与属性,建议将声明为val
而不是var
使用不可变集合接口(Collection,List,Set,Map)来声明无需改变的集合。
使用工厂函数创建集合实例时,尽可能使用不可变集合类型的函数
// 不好的使用方法:使用可变集合类型作为无需改变的值
fun validateValue(actualValue: String, allowedValues: HashSet<String>) { …… }
// 正确的使用方法:使用不可变集合类型
fun validateValue(actualValue: String, allowedValues: Set<String>) { …… }
// 不好的使用方法:arrayListOf() 返回 ArrayList<T>,这是一个可变集合类型
val allowedValues = arrayListOf("a", "b", "c")
// 正确的使用方法:listOf() 返回 List<T>
val allowedValues = listOf("a", "b", "c")
默认参数值
优先声明带有默认参数的函数,而不是声明重载函数。
例如:
fun foo(a: String = "a") { /*……*/ }
Lambda 表达式参数
在简短、非嵌套的 lambda 表达式中建议使用 it
用法而不是显式声明参数。而在有参数的嵌套 lambda 表达式中,始终应该显式声明参数。
避免在 lambda 表达式中使用多个返回到标签。请考虑重新组织这样的 lambda 表达式使其只有单一退出点。 如果这无法做到或者不够清晰,请考虑将 lambda 表达式转换为匿名函数。
不要在 lambda 表达式的最后一条语句中使用返回到标签。
使用条件语句
优先使用 try
,if
,与when
的表达形式
例如:
return if(x) foo() else bar()
return when(x){
0 -> "zero"
else -> "nonzero"
}
//而不是:
if (x)
return foo()
else
return bar()
when(x) {
0 -> return "zero"
else -> return "nonzero"
}
优先使用if
在二元条件之中,优先使用if
而不是When
例如:
when (x) {
null -> // ……
else -> // ……
}
//建议改为
if (x == null) …… else ……
只有在三元条件下,才优先使用when
循环
优先使用高阶函数(filter,map等),而不是循环。
当在使用多个高阶函数的复杂表达式与循环之间进行选择时,请了解每种情况下所执行操作的开销并且记得考虑性能因素。
字符串
尽量使用字符串模板,而不是字符串拼接。
优先使用多行字符串,而不要使用“\n”
转义字符,实现换行。
使用扩展函数
放手去用扩展函数。每当你有一个主要用于某个对象的函数时,可以考虑使其成为一个以该对象为接收者的扩展函数。为了尽量减少 API 污染,尽可能地限制扩展函数的可见性。根据需要,使用局部扩展函数、成员扩展函数或者具有私有可视性的顶层扩展函数。
平台类型
返回平台类型表达式的公有函数/方法必须显式声明其 Kotlin 类型:
例如:
fun apiCall(): String = MyJavaApi.getProperty("name")
任何使用平台类型表达式初始化的属性(包级别或类级别)必须显式声明其 Kotlin 类型:
class Person {
val name: String = MyJavaApi.getProperty("name")
}
作用域函数 apply/with/run/also/let
Kotlin 提供了一系列用来在给定对象上下文中执行代码块的函数:let
、 run
、 with
、 apply
以及 also
。--- 有机会详细了解一下 作用域函数的意义吧。
库的编码规范
在编写库时,建议遵循一组额外的规则以确保 API 的稳定性:
- 总是显式指定成员的可见性(以避免将声明意外暴露为公有 API )
- 总是显式指定函数返回类型以及属性类型(以避免当实现改变时意外更改返回类型)
- 为所有公有成员提供 KDoc 注释,不需要任何新文档的覆盖成员除外 (以支持为该库生成文档
评论区