Android Kotlin 編碼規範
編碼規範
本頁包含當前Kotlin 語言的編碼風格。
應用風格指南
如需根據本風格指南配置IntelliJ格式化程序,請安裝Kotlin插件1.2.20或更高版本,轉到“Settings | Editor | Code Style | Kotlin”,點擊右上角的“Set from…”鏈接,並從菜單中選擇“Predefined style / Kotlin style guide”。
如需驗證代碼已按風格指南格式化,請轉到探查設置(Inspections)並啟用“Kotlin | Style issues | File is not formatted according to project settings”探查項。驗證風格指南中描述的其他問題(如命名約定)的附加探查項默認已啟用。
源代碼組織
目錄結構
在混合語言項目中,Kotlin 源文件應當與Java 源文件位於同一源文件根目錄下, 並遵循相同的目錄結構(每個文件應存儲在與其package 語句對應的目錄中)。
在純Kotlin項目中,推薦的目錄結構遵循省略了公共根包的包結構(例如,如果項目中的所有代碼都位於“org.example.kotlin”包及其子包中,那麼“org.example. kotlin”包的文件應該直接放在源代碼根目錄下,而“org.example.kotlin.foo.bar”中的文件應該放在源代碼根目錄下的“foo/bar”子目錄中)。
源文件名稱
如果Kotlin文件包含單個類(以及可能相關的頂層聲明),那麼文件名應該與該類的名稱相同,並追加.kt擴展名。如果文件包含多個類或只包含頂層聲明,那麼選擇一個描述該文件所包含內容的名稱,並以此命名該文件。使用首字母大寫的駝峰風格(例如
ProcessDeclarations.kt
)。
文件的名稱應該描述文件中代碼的作用。因此,應避免在文件名中使用諸如“Util”之類的無意義詞語。
源文件組織
鼓勵多個聲明(類、頂級函數或者屬性)放在同一個Kotlin 源文件中, 只要這些聲明在語義上彼此緊密關聯並且文件保持合理大小(不超過幾百行)。
特別是在為類定義與類的所有客戶都相關的擴展函數時,請將它們放在與類自身定義相同的地方。而在定義僅對指定客戶有意義的擴展函數時,請將它們放在緊挨該客戶代碼之後。不要只是為了保存“Foo的所有擴展函數”而創建文件。
類佈局
通常,一個類的內容按以下順序排列:
- 屬性聲明與初始化塊
- 次構造函數
- 方法聲明
- 伴生對象
不要按字母順序或者可見性對方法聲明排序,也不要將常規方法與擴展方法分開。而是要把相關的東西放在一起,這樣從上到下閱讀類的人就能夠跟進所發生事情的邏輯。選擇一個順序(高級別優先,或者相反)並堅持下去。
將嵌套類放在緊挨使用這些類的代碼之後。如果打算在外部使用嵌套類,而且類中並沒有引用這些類,那麼把它們放到末尾,在伴生對象之後。
接口實現佈局
在實現一個接口時,實現成員的順序應該與該接口的成員順序相同(如果需要, 還要插入用於實現的額外的私有方法)
重載佈局
在類中總是將重載放在一起。
命名規則
Kotlin 遵循Java 命名約定。尤其是:
包的名稱總是小寫且不使用下劃線(
org.example.myproject
)。通常不鼓勵使用多個詞的名稱,但是如果確實需要使用多個詞,可以將它們連接在一起或使用駝峰(org.example.myProject
)。
類與對象的名稱以大寫字母開頭並使用駝峰:
函數名
函數、屬性與局部變量的名稱以小寫字母開頭、使用駝峰而不使用下劃線:
例外:用於創建類實例的工廠函數可以與要創建的類具有相同的名稱:
測試方法的名稱
當且僅當在測試中,可以使用反引號括起來的帶空格的方法名。(請注意,Android運行時目前不支持這樣的方法名。)測試代碼中也允許方法名使用下劃線。
屬性名
常量名稱(標有
const
的屬性,或者保存不可變數據的沒有自定義get
函數的頂層/對象val
屬性)應該使用大寫、下劃線分隔的名稱:
保存帶有行為的對像或者可變數據的頂層/對象屬性的名稱應該使用常規駝峰名稱:
保存單例對象引用的屬性的名稱可以使用與
object
聲明相同的命名風格:
對於枚舉常量,可以使用大寫、下劃線分隔的名稱(
enum class Color { RED, GREEN }
)也可使用以大寫字母開頭的常規駝峰名稱,具體取決於用途。幕後屬性的名稱
如果一個類有兩個概念上相同的屬性,一個是公共API的一部分,另一個是實現細節,那麼使用下劃線作為私有屬性名稱的前綴:
選擇好名稱
類的名稱通常是用來解釋類是什麼的名詞或者名詞短語:
List
、PersonReader
。
方法的名稱通常是動詞或動詞短語,說明該方法做什麼:
close
、readPersons
。修改對像或者返回一個新對象的名稱也應遵循建議。例如sort
是對一個集合就地排序,而sorted
是返回一個排序後的集合副本。
名稱應該表明實體的目的是什麼,所以最好避免在名稱中使用無意義的單詞(
Manager
、Wrapper
等)。
當使用首字母縮寫作為名稱的一部分時,如果縮寫由兩個字母組成,就將其大寫(
IOStream
);而如果縮寫更長一些,就隻大寫其首字母(XmlFormatter
、HttpInputStream
)。格式化
在大多數情況下,Kotlin 遵循Java 編碼規範。
使用4 個空格縮進。不要使用tab。
對於花括號,將左花括號放在結構起始處的行尾,而將右花括號放在與左括結構橫向對齊的單獨一行。
(注意:在Kotlin 中,分號是可選的,因此換行很重要。語言設計採用Java 風格的花括號格式,如果嘗試使用不同的格式化風格,那麼可能會遇到意外的行為。)
橫向空白
在二元操作符左右留空格(
a + b
)。例外:不要在“range to”操作符(0..i
)左右留空格。
不要在一元運算符左右留空格(
a++
)
在控制流關鍵字(
if
、when
、for
以及while
)與相應的左括號之間留空格。
不要在主構造函數聲明、方法聲明或者方法調用的左括號之前留空格。
絕不在
(
、[
之後或者]
、)
之前留空格。
絕不在
.
或者?.
左右留空格:foo.bar().filter { it > 2 }.joinToString()
,foo?.bar()
在
//
之後留一個空格:// 这是一条注释
不要在用於指定類型參數的尖括號前後留空格:
class Map { …… }
不要在
::
前後留空格:Foo::class
、String::length
不要在用於標記可空類型的
?
前留空格:String?
作為一般規則,避免任何類型的水平對齊。將標識符重命名為不同長度的名稱不應該影響聲明或者任何用法的格式。
冒號
在以下場景中的
:
之前留一個空格:- 當它用於分隔類型與超類型時;
- 當委託給一個超類的構造函數或者同一類的另一個構造函數時;
- 在
object
關鍵字之後。
而當分隔聲明與其類型時,不要在
:
之前留空格。
在
:
之後總要留一個空格。類頭格式化
具有少數主構造函數參數的類可以寫成一行:
具有較長類頭的類應該格式化,以使每個主構造函數參數都在帶有縮進的獨立的行中。另外,右括號應該位於一個新行上。如果使用了繼承,那麼超類的構造函數調用或者所實現接口的列表應該與右括號位於同一行:
對於多個接口,應該將超類構造函數調用放在首位,然後將每個接口應放在不同的行中:
對於具有很長超類型列表的類,在冒號後面換行,並橫向對齊所有超類型名:
為了將類頭與類體分隔清楚,當類頭很長時,可以在類頭後放一空行(如上例所示)或者將左花括號放在獨立行上:
構造函數參數使用常規縮進(4 個空格)。
理由:這確保了在主構造函數中聲明的屬性與在類體中聲明的屬性具有相同的縮進。
修飾符
如果一個聲明有多個修飾符,請始終按照以下順序安放:
將所有註解放在修飾符前:
除非你在編寫庫,否則請省略多餘的修飾符(例如
public
)。註解格式化
註解通常放在單獨的行上,在它們所依附的聲明之前,並使用相同的縮進:
無參數的註解可以放在同一行:
無參數的單個註解可以與相應的聲明放在同一行:
文件註解
文件註解位於文件註釋(如果有的話)之後、
package
語句之前,並且用一個空白行與package
分開(為了強調其針對文件而不是包)。函數格式化
如果函數簽名不適合單行,請使用以下語法:
函數參數使用常規縮進(4 個空格)。
理由:與構造函數參數一致
對於由單個表達式構成的函數體,優先使用表達式形式。
表達式函數體格式化
如果函數的表達式函數體與函數聲明不適合放在同一行,那麼將
=
留在第一行。將表達式函數體縮進4個空格。屬性格式化
對於非常簡單的只讀屬性,請考慮單行格式:
對於更複雜的屬性,總是將
get
與set
關鍵字放在不同的行上:
對於具有初始化器的屬性,如果初始化器很長,那麼在等號後增加一個換行並將初始化器縮進四個空格:
格式化控制流語句
如果
if
或when
語句的條件有多行,那麼在語句體外邊總是使用大括號。將該條件的每個後續行相對於條件語句起始處縮進4個空格。將該條件的右圓括號與左花括號放在單獨一行:理由:對齊整齊並且將條件與語句體分隔清楚
將
else
、catch
、finally
關鍵字以及do/while循環的while
關鍵字與之前的花括號放在相同的行上:
在
when
語句中,如果一個分支不止一行,可以考慮用空行將其與相鄰的分支塊分開:
將短分支放在與條件相同的行上,無需花括號。
方法調用格式化
在較長參數列表的左括號後添加一個換行符。按4 個空格縮進參數。將密切相關的多個參數分在同一行。
在分隔參數名與值的
=
左右留空格。鍊式調用換行
當對鍊式調用換行時,將
.
字符或者?.
操作符放在下一行,並帶有單倍縮進:
調用鏈的第一個調用通常在換行之前,當然如果能讓代碼更有意義也可以忽略這點。
Lambda 表達式格式化
在lambda 表達式中,應該在花括號左右以及分隔參數與代碼體的箭頭左右留空格。如果一個調用接受單個lambda 表達式,應該盡可能將其放在圓括號外邊傳入。
如果為lambda 表達式分配一個標籤,那麼不要在該標籤與左花括號之間留空格:
在多行的lambda 表達式中聲明參數名時,將參數名放在第一行,後跟箭頭與換行符:
如果參數列表太長而無法放在一行上,請將箭頭放在單獨一行:
文檔註釋
對於較長的文檔註釋,將開頭
/**
放在一個獨立行中,並且後續每行都以星號開頭:
簡短註釋可以放在一行內:
通常,避免使用
@param
與@return
標記。而是將參數與返回值的描述直接合併到文檔註釋中,並在提到參數的任何地方加上參數鏈接。只有當需要不適合放進主文本流程的冗長描述時才應使用@param
與@return
。避免重複結構
一般來說,如果Kotlin 中的某種語法結構是可選的並且被IDE 高亮為冗餘的,那麼應該在代碼中省略之。為了清楚起見,不要在代碼中保留不必要的語法元素。
Unit
如果函數返回Unit,那麼應該省略返回類型:
分號
盡可能省略分號。
字符串模版
將簡單變量傳入到字符串模版中時不要使用花括號。只有用到更長表達式時才使用花括號。
語言特性的慣用法
不可變性
優先使用不可變(而不是可變)數據。初始化後未修改的局部變量與屬性,總是將其聲明為
val
而不是var
。
總是使用不可變集合接口(
Collection
, List
, Set
, Map
)來聲明無需改變的集合。使用工廠函數創建集合實例時,盡可能選用返回不可變集合類型的函數:默認參數值
優先聲明帶有默認參數的函數而不是聲明重載函數。
類型別名
如果有一個在代碼庫中多次用到的函數類型或者帶有類型參數的類型,那麼最好為它定義一個類型別名:
Lambda 表達式參數
在簡短、非嵌套的lambda表達式中建議使用
it
用法而不是顯式聲明參數。而在有參數的嵌套lambda表達式中,始終應該顯式聲明參數。在lambda 表達式中返回
避免在lambda 表達式中使用多個返回到標籤。請考慮重新組織這樣的lambda 表達式使其只有單一退出點。如果這無法做到或者不夠清晰,請考慮將lambda 表達式轉換為匿名函數。
不要在lambda 表達式的最後一條語句中使用返回到標籤。
命名參數
當一個方法接受多個相同的原生類型參數或者多個
Boolean
類型參數時,請使用命名參數語法,除非在上下文中的所有參數的含義都已絕對清楚。使用條件語句
優先使用
try
、if
與when
的表達形式。例如:
優先選用上述代碼而不是:
if
還是 when
二元條件優先使用
if
而不是when
。不要使用
而應使用
if (x == null) …… else ……
如果有三個或多個選項時優先使用
when
。
在條件中使用可空的Boolean
值
如果需要在條件語句中用到可空的
Boolean
,使用if (value == true)
或if (value == false)
檢測。使用循環
優先使用高階函數(
filter
、map
等)而不是循環。例外:forEach
(優先使用常規的for
循環,除非forEach
的接收者是可空的或者forEach
用做長調用鏈的一部分。)
當在使用多個高階函數的複雜表達式與循環之間進行選擇時,請了解每種情況下所執行操作的開銷並且記得考慮性能因素。
區間上循環
使用
until
函數在一個區間上循環:使用字符串
優先使用字符串模板而不是字符串拼接。
優先使用多行字符串而不是將
\n
轉義序列嵌入到常規字符串字面值中。
如需在多行字符串中維護縮進,當生成的字符串不需要任何內部縮進時使用
trimIndent
,而需要內部縮進時使用trimMargin
:函數還是屬性
在某些情況下,不帶參數的函數可與只讀屬性互換。雖然語義相似,但是在某種程度上有一些風格上的約定。
底層算法優先使用屬性而不是函數:
- 不會拋異常
- 計算開銷小(或者在首次運行時緩存)
- 如果對象狀態沒有改變,那麼多次調用都會返回相同結果
使用擴展函數
放手去用擴展函數。每當你有一個主要用於某個對象的函數時,可以考慮使其成為一個以該對象為接收者的擴展函數。為了盡量減少API污染,盡可能地限制擴展函數的可見性。根據需要,使用局部擴展函數、成員擴展函數或者俱有私有可視性的頂層擴展函數。
使用中綴函數
一個函數只有用於兩個角色類似的對象時才將其聲明為中綴函數。良好示例如:
and
、to
、zip
。不良示例如:add
。
如果一個方法會改動其接收者,那麼不要聲明為中綴形式。
工廠函數
如果為一個類聲明一個工廠函數,那麼不要讓它與類自身同名。優先使用獨特的名稱, 該名稱能表明為何該工廠函數的行為與眾不同。只有當確實沒有特殊的語義時, 才可以使用與該類相同的名稱。
例如:
如果一個對像有多個重載的構造函數,它們並非調用不同的超類構造函數,並且不能簡化為具有默認參數值的單個構造函數,那麼優先用工廠函數取代這些重載的構造函數。
平台類型
返回平台類型表達式的公有函數/方法必須顯式聲明其Kotlin 類型:
任何使用平台類型表達式初始化的屬性(包級別或類級別)必須顯式聲明其Kotlin 類型:
使用平台類型表達式初始化的局部值可以有也可以沒有類型聲明:
使用作用域函數apply/with/run/also/let
Kotlin提供了一系列用來在給定對像上下文中執行代碼塊的函數。要選擇正確的函數,請考慮以下幾點:
- 是否在塊中的多個對像上調用方法,或者將上下文對象的實例作為參數傳遞?如果是,那麼使用以
it
而不是this
形式訪問上下文對象的函數之一(also
或let
)。如果在代碼塊中根本沒有用到接收者,那麼使用also
。
- 調用的結果是什麼?如果結果需是該上下文對象,那麼使用
apply
或also
。如果需要從代碼塊中返回一個值,那麼使用with
、let
或者run
- 上下文對像是否可空,或者是否作為調用鏈的結果求值而來的?如果是,那麼使用
apply
、let
或者run
。否則,使用with
或者also
。
庫的編碼規範
在編寫庫時,建議遵循一組額外的規則以確保API 的穩定性:
- 總是顯式指定成員的可見性(以避免將聲明意外暴露為公有API )
- 總是顯式指定函數返回類型以及屬性類型(以避免當實現改變時意外更改返回類型)
- 為所有公有成員提供KDoc 註釋,不需要任何新文檔的覆蓋成員除外(以支持為該庫生成文檔)