公式の和訳(意訳)です.
- IDEでスタイルを設定する, ソースコード構成
- 命名規約
- フォーマット 前半
- フォーマット 後半
- ドキュメントコメント,冗長な記述を避ける
- 慣用表現の使用(この記事)
- ライブラリのコード規約
慣用表現の使用
不変性
可変なデータより不変なデータを使う.
ローカル変数やプロパティの宣言をするときに,初期化より後に値を変更しないなら,var
ではなくval
を使う.
値を変更しないコレクションを宣言するときは,不変なコレクションのインターフェース(Collection
, List
, Set
, Map
)を使う.
コレクションのインスタンスを作るファクトリ関数を使うときは,出来る限り不変なコレクション型を返す関数を使う.
// Bad: use of mutable collection type for value which will not be mutated
fun validateValue(actualValue: String, allowedValues: HashSet<String>) { ... }
// Good: immutable collection type used instead
fun validateValue(actualValue: String, allowedValues: Set<String>) { ... }
// Bad: arrayListOf() returns ArrayList<T>, which is a mutable collection type
val allowedValues = arrayListOf("a", "b", "c")
// Good: listOf() returns List<T>
val allowedValues = listOf("a", "b", "c")
デフォルトパラメータ
オーバーロードする関数を宣言するときは,デフォルトパラメータを使う.
// Bad
fun foo() = foo("a")
fun foo(a: String) { ... }
// Good
fun foo(a: String = "a") { ... }
型エイリアス
コード上で何度も使うような関数型や型パラメータつきの型があるときは,型パラメータを使う.
typealias MouseClickHandler = (Any, MouseEvent) -> Unit
typealias PersonIndex = Map<String, Person>
ラムダ式のパラメータ
短くてネストしていないラムダ式では,パラメータを明示的に宣言するのではなく,it
を使う.
ネストされたラムダ式では,パラメータを明示的に宣言する.
ラムダ式の返り値
ラムダ式で複数のラベルつき return を使わない.
ラムダ式を書き直して,ある場所にだけ return するようにする.
それが可能でなかったり,明示的にならないときは,ラムダ式を匿名関数に変換する.
ラムダ式内の最後の式にラベルつき return を使わない.
名前付き引数
同じプリミティブ型の引数が複数あるときや Boolean
型のパラメータがあるときは,名前付き引数を使う.
ただし,すべてのパラメータの意味がコンテキストから完全に明らかなときは,使う必要はない.
drawSquare(x = 10, y = 10, width = 100, height = 100, fill = true)
条件式の使用
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 vs when
条件分岐が2通りのときは,when
ではなくif
を使う.
when (x) {
null -> ...
else -> ...
}
上の例よりは, if (x == null) ... else ...
のほうが適切である.
条件分岐が3通り以上あるときは,when
を使う.
条件に null 許容型の Boolean を使うとき
条件式に null 許容型の Boolean を使うときは, if (value == true)
か if (value == false)
で判定する.
ループを使うとき
高階関数(filter
, map
など)を使ってループを書く.
例外:foreach
(代わりに,普通のfor
によるループを使う.
forEach
のレシーバが null 許容型であったり,とても長いメソッドチェーンの一部であるときは,forEach
を使ってもよい).
複数の高階関数とループを使った複雑な式を書くときは,それぞれの演算の性能を考慮し,パフォーマンスを念頭に置く.
範囲によるループ
端を含まない範囲をループする時は until
を使う.
for (i in 0..n - 1) { ... } // bad
for (i in 0 until n) { ... } // good
文字列を使うとき
文字列の連結ではなく, String テンプレートを使う.
通常の文字列リテラルに \n
を埋め込むのではなく,複数行の文字列を使う.
複数行の文字列のインデントを整備するにあたって,結果の文字列にインデントが不要なときは trimIndent
を使い,インデントが必要なときは trimMargin
を使う.
関数 vs プロパティ
引数なしの関数は読み込み専用プロパティに変換可能である場合がある.
意味的には似ているが,どちらを選ぶか文体的な規則がある.
関数よりプロパティを選ぶのは,以下のような場合である.
- 例外を投げない
- 計算が簡単である(あるいは,最初の実行時に結果がキャッシュされる)
- オブジェクトの状態が変わらない限り,同じ結果を返す
拡張関数を使う
拡張関数を自由に使う.
あるオブジェクトに対して作用する関数を書くときはいつでも,そのオブジェクトをレシーバとする拡張関数に書き直すことを検討する.
APIの汚染を最小限にとどめるため,拡張関数の可視性は妥当な範囲に留める.
必要に応じて,ローカル拡張関数やメンバ拡張関数,トップレベルの拡張関数を private にして使う.
infix 関数を使うとき
2つのオブジェクトが同じような役割を果たすときだけ,関数に infix
をつける.
良い例: and
, to
, zip
.
悪い例: add
.
レシーバオブジェクトを変更するようなメソッドには infix をつけない.
ファクトリ関数
クラスのファクトリ関数を宣言するとき,クラスと同じ名前をつけないようにする.
ファクトリ関数の動作が特別である理由が明らかになるような名前をつける.
本当に特別な意味がないときだけクラスと同じ名前をつけてもよい.
class Point(val x: Double, val y: Double) {
companion object {
fun fromPolar(angle: Double, radius: Double) = Point(...)
}
}
それぞれが親クラスの同じコンストラクタを呼び出していて,デフォルト引数をもつひとつのコンストラクタにまとめることも難しいような,複数のオーバーロードしたコンストラクタをもつオブジェクトがあるときは,オーバーロードしたコンストラクタをファクトリ関数に置き換えるとよい.
プラットフォーム型
プラットフォーム型をの式返す public な関数・メソッドは必ず Kotlin の型を明示する.
fun apiCall(): String = MyJavaApi.getProperty("name")
プラットフォーム型の式で初期化するすべてのプロパティ(パッケージレベルやクラスレベル)は必ず Kotlin の型を明示する.
class Person {
val name: String = MyJavaApi.getProperty("name")
}
プラットフォーム型の式で初期化するローカル変数は型を明示してもしなくてもいい.
fun main(args: Array<String>) {
val name = MyJavaApi.getProperty("name")
println(name)
}
スコープ関数 apply/with/run/also/let を使う
Kotlin にはあるオブジェクトのコンテキストでコードブロックを実行できる関数セットがある: let
, run
, with
, aply
, also
.
これらのスコープ関数の適切な利用には次のドキュメントを参照のこと: Scope Functions