侧边栏壁纸
博主头像
Z同学博主等级

工作磨平激情前,坚持技术的热忱。 欢迎光临Z同学的技术小站。 分享最新的互联网知识。

  • 累计撰写 283 篇文章
  • 累计创建 55 个标签
  • 累计收到 93 条评论

Kotlin 编码规范

Z同学
2021-07-19 / 0 评论 / 2 点赞 / 802 阅读 / 5,060 字
温馨提示:
本文最后更新于 2022-01-17,若内容或图片失效,请留言反馈。部分素材来自网络,若不小心影响到您的利益,请联系我们删除。

前言:

本篇内容介绍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文件。

类布局

通常,一个类的内部内容按照以下顺序排列:

  1. 属性声明与初始化块
  2. 次构造器
  3. 方法声明
  4. 伴生对象

不要按照字母顺序或者可见性对方法声明排序,也不要将常规方法与扩展方法分开。

而是要将相关的东西放在一起。这样从上到下阅读类的人就能够跟进所发生事情的逻辑。选择一个顺序(高级别优先,或者相反)并一直坚持下去。

将嵌套类放在紧挨使用这些类的代码之后。

如果打算在外部使用嵌套类,而类中并没有引用这些类,那么将它们放在末尾,在伴生对象之后。

接口实现布局

在实现一个接口时,实现函数的顺序应该与该接口的成员顺序相同。

重载布局

在类中,将重载放在一起。

命名规范

在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


控制流语句

如果 ifwhen 语句的条件有多行,那么在语句体外边总是使用大括号。 将该条件的每个后续行相对于条件语句起始处缩进 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 提供了一系列用来在给定对象上下文中执行代码块的函数:letrunwithapply 以及 also。--- 有机会详细了解一下 作用域函数的意义吧。

库的编码规范

在编写库时,建议遵循一组额外的规则以确保 API 的稳定性:

  • 总是显式指定成员的可见性(以避免将声明意外暴露为公有 API )
  • 总是显式指定函数返回类型以及属性类型(以避免当实现改变时意外更改返回类型)
  • 为所有公有成员提供 KDoc 注释,不需要任何新文档的覆盖成员除外 (以支持为该库生成文档
2

评论区