このドキュメントは、Kotlin プログラミング言語でのソースコードの記述について Google が推奨する Android コーディング標準の定義を完全に網羅しています。Kotlin ソースファイルを Google Android スタイルとして記述するためには、必ずここに記載されているルールに従う必要があります。
他のプログラミング スタイルガイドと同様に、読みやすいフォーマットだけでなく、他の種類の規則やコーディング標準も扱っています。ただし、このドキュメントでは、主として絶対に守るべき厳格なルールに焦点を絞り、(手動であれツールによってであれ)明らかに適用すべきとはいえない助言は行いません。
ソースファイル
ソースファイルはすべて UTF-8 としてエンコードする必要があります。
命名
ソースファイルにトップレベル クラスが 1 つしか含まれていない場合、ファイル名には、名前(大文字と小文字を区別します)と .kt
拡張子を反映させる必要があります。あるいは、ソースファイルに複数のトップレベル宣言が含まれている場合は、ファイルの内容を表す名前を選択し、PascalCase(ファイル名が複数形の場合はキャメルケースを使用可)を適用し、.kt
拡張子を追加します。
// MyClass.kt class MyClass { }
// Bar.kt class Bar { } fun Runnable.toBar(): Bar = // …
// Map.kt fun <T, O> Set<T>.map(func: (T) -> O): List<O> = // … fun <T, O> List<T>.map(func: (T) -> O): List<O> = // …
// extensions.kt fun MyClass.process() = // … fun MyResult.print() = // …
特殊文字
空白文字
行末シーケンスを除き、ソースファイルに使用できる空白文字は ASCII 水平スペース文字(0x20)のみです。 つまり、次のようになります。
- 文字列内のその他すべての空白文字と文字リテラルはエスケープされます。
- インデントにタブ文字は使用されません。
特殊なエスケープ シーケンス
特殊なエスケープ シーケンスを持つ文字(\b
、\n
、\r
、\t
、\'
、\"
、\\
、\$
)の場合、対応する Unicode(例: \u000a
)エスケープではなくそのシーケンスが使用されます。
非 ASCII 文字
それ以外の非 ASCII 以外の文字については、実際の Unicode 文字(例: ∞
)または同等の Unicode エスケープ(例: \u221e
)のいずれかが使用されます。どちらを使用するかは、コードが読みやすくわかりやすいものという基準のみに基づいて選択します。
Unicode エスケープは、いかなる場所でも、印刷用文字には使用しないでください。また、文字列リテラルおよびコメント以外では使用しないでください。
例 | 評価 |
---|---|
val unitAbbrev = "μs" |
最適: コメントなしでも完全に明快にわかります。 |
val unitAbbrev = "\u03bcs" // μs |
要改善: 印刷用文字にエスケープを使用すべき理由はありません。 |
val unitAbbrev = "\u03bcs" |
要改善: これが何なのか、読む人にはわかりません。 |
return "\ufeff" + content |
良好: 印刷用以外の文字にエスケープを使用し、必要に応じてコメントを使用します。 |
構造
.kt
ファイルは次の要素で構成され、各要素は次の順序で配置されます。
- 著作権および / またはライセンス ヘッダー(オプション)
- ファイルレベルのアノテーション
- パッケージ ステートメント
- インポート ステートメント
- トップレベル宣言
これら各セクションは、厳密に 1 行の空白行によって区切られます。
著作権 / ライセンス
ファイルに著作権またはライセンスのヘッダーがある場合は、複数行のコメントの先頭に配置します。
/* * Copyright 2017 Google, Inc. * * ... */
KDoc スタイルまたは単一行スタイルのコメントは使用しないでください。
/** * Copyright 2017 Google, Inc. * * ... */
// Copyright 2017 Google, Inc. // // ...
ファイルレベルのアノテーション
ファイル「use-site target」を伴うアノテーションは、ヘッダー コメントとパッケージ宣言の間に配置されます。
パッケージ ステートメント
パッケージ ステートメントには列の制限は適用されず、行が折り返されることはありません。
インポート ステートメント
クラス、関数、プロパティのインポート ステートメントは、1 つのリストにまとめられ、ASCII コード順で並べ替えられます。
ワイルドカードのインポートは、種類を問わず、使用できません。
パッケージ ステートメントと同様、インポート ステートメントには列の制限は適用されず、行が折り返されることはありません。
トップレベル宣言
.kt
ファイルは、トップレベルで 1 つ以上の型、関数、プロパティ、型エイリアスを宣言できます。
ファイルの内容は 1 つのテーマに絞る必要があります。たとえば、複数の受信者型に対して同じオペレーションを実行する拡張関数セットや単一のパブリック型などです。関連のない宣言はそれぞれ別のファイルに分割し、1 ファイル内のパブリック宣言を最小限に抑える必要があります。
ファイルの内容の数や順序には、明示的な制限はありません。
ソースファイルは通常、上から下に読み込まれます。つまり、この順序には一般的に、上位の宣言によって下位の解釈が通知されるという状況が反映されます。ファイルが異なると、内容の順序も異なる場合があります。同様に、あるファイルには 100 個のプロパティが含まれ、別のファイルには 10 個の関数、さらに別のファイルには 1 個のクラスが含まれる場合があります。
重要なのは、各クラスになんらかの論理的順序を使用して、問われた場合には管理者がそれを説明できるようにしておくことです。たとえば、新しい関数はいつもファイルの最後に追加されるとは限りません。いつも最後に追加すると「追加日付による時系列」が生じることになり、それは論理的な順序付けではないからです。
クラスメンバーの順序
クラス内のメンバーの順序は、トップレベル宣言と同じルールに従います。
形式
中かっこ
else
ブランチが 1 つ以下で 1 行に収まる if
の式および when
ブランチには、中かっこは必須ではありません。
if (string.isEmpty()) return val result = if (string.isEmpty()) DEFAULT_VALUE else string when (value) { 0 -> return // … }
それ以外は、本文が空白またはステートメントを 1 つしか含まない場合でも、if
、for
、when
ブランチ、do
、while
のステートメントと式には中かっこが必要です。
if (string.isEmpty()) return // WRONG! if (string.isEmpty()) { return // Okay } if (string.isEmpty()) return // WRONG else doLotsOfProcessingOn(string, otherParametersHere) if (string.isEmpty()) { return // Okay } else { doLotsOfProcessingOn(string, otherParametersHere) }
空白でないブロック
空白でないブロックおよびブロック状構造の中かっこは、次のように Kernighan&Ritchie スタイル(いわゆる「エジプト人ブレース」)に従います。
- 左中かっこの前には改行を入れません。
- 左中かっこの後に改行を入れます。
- 右中かっこの前に改行を入れます。
- ステートメントが右中かっこで終了する場合、または関数の本文、コンストラクタ、名前付きクラスが右中かっこで終了する場合のみ、その右中かっこの後に改行を入れます。たとえば、中かっこの後に
else
またはカンマがある場合は改行を入れません。
return Runnable { while (condition()) { foo() } } return object : MyClass() { override fun foo() { if (condition()) { try { something() } catch (e: ProblemException) { recover() } } else if (otherCondition()) { somethingElse() } else { lastThing() } } }
enum クラスの例外をいくつか以下に示します。
空白ブロック
空白のブロックまたはブロック状構造は、K&R スタイルにする必要があります。
try { doSomething() } catch (e: Exception) {} // WRONG!
try { doSomething() } catch (e: Exception) { } // Okay
式
式として使用される if/else
条件では、式全体が 1 行に収まる場合に限り、中かっこを省略できます。
val value = if (string.isEmpty()) 0 else 1 // Okay
val value = if (string.isEmpty()) // WRONG! 0 else 1
val value = if (string.isEmpty()) { // Okay 0 } else { 1 }
インデント
新たなブロックまたはブロック状構造が開かれるたびに、インデントが 4 スペース分増加します。ブロックが終了すると、インデントは以前のインデント レベルに戻ります。インデント レベルは、ブロック全体を通してコードとコメントの両方に適用されます。
1 行に 1 つずつ
各ステートメントの後に改行が続きます。セミコロンは使用されません。
行の折り返し
コードの列は 100 文字に制限されます。以下の場合を除き、この制限を超える行には、下の説明に沿って行の折り返しを行う必要があります。
例外:
- 列の制限に従うことが不可能な行(例: KDoc の長い URL)
package
およびimport
ステートメント- シェルに切り取り&貼り付けされる可能性のあるコメント内コマンドライン
改行箇所
行の折り返しに関する主要な指示: より高い構文レベルでの改行を優先。別の指示:
- 演算子または infix 関数名の箇所で改行される場合、改行は演算子または infix 関数名の後に入ります。
- 次の「演算子に似た」記号の箇所で改行される場合、改行は記号の前に入ります。
- ドット区切り文字(
.
、?.
)。 - メンバー参照の 2 つのコロン(
::
)。
- ドット区切り文字(
- メソッドまたはコンストラクタの名前は、その後の左かっこ(
(
)に付いたままになります。 - カンマ(
,
)は、その前のトークンに付いたままになります。 - ラムダ矢印(
->
)は、その前の引数リストに付いたままになります。
関数
関数の署名が 1 行に収まらない場合は、各パラメータ宣言ごとに改行します。この形式で定義されたパラメータには、シングル インデント(+4)を使用します。右かっこ()
)と戻り値の型は、インデントを追加せず、それぞれ別の行に配置されます。
fun <T> Iterable<T>.joinToString( separator: CharSequence = ", ", prefix: CharSequence = "", postfix: CharSequence = "" ): String { // … }
式関数
式を 1 つしか含まない関数は、式関数として表すことができます。
override fun toString(): String { return "Hey" }
override fun toString(): String = "Hey"
プロパティ
プロパティ イニシャライザが 1 行に収まらない場合は、等号(=
)の後で改行し、インデントを使用します。
private val defaultCharset: Charset? = EncodingRegistry.getInstance().getDefaultCharsetForPropertiesFiles(file)
get
または set
、あるいはその両方の関数を宣言するプロパティは、通常のインデント(+4)を付けて、それぞれを別々の行に配置します。関数と同じルールを使用した形式にします。
var directory: File? = null set(value) { // … }読み取り専用プロパティでは、1 行に収まる短い構文を使用できます。
val defaultExtension: String get() = "kt"
空白文字
垂直
空白行が 1 つ表示されます。
- 連続したクラスメンバー間: プロパティ、コンストラクタ、関数、ネストされたクラスなど
- 例外: 連続した 2 つのプロパティ間(間に他のコードがない場合)の空白行は省略可能です。このような空白行は、必要に応じて、プロパティを論理的にグループ化し、バッキング プロパティが存在する場合にそれと関連付けるために使用します。
- 例外: enum 定数間の空白行については下で説明します。
- コードを論理的なサブセクションに整理する必要がある場合の、ステートメントとステートメントの間。
- 必要に応じて、関数の最初のステートメントの前、クラスの最初のメンバーの前、またはクラスの最後のメンバーの後(推奨事項でも非推奨事項でもありません)。
- このドキュメントの他のセクション(構造に関するセクションなど)で必要とされる場合。
複数の空白行を連続して使用することは可能ですが、必須ではなく、推奨されません。
水平
言語または他のスタイルルールで必要とされる以外に、およびリテラル、コメント、KDoc とは別に、次の箇所に限って ASCII スペース 1 文字も使用されます。
- 予約語(
if
など)の区切りfor
またはcatch
その行の後の開きかっこ((
)から返されます。// WRONG! for(i in 0..1) { }
// Okay for (i in 0..1) { }
else
またはcatch
などの予約語を、行の上でその前に来る右中かっこ(}
)と区切る箇所。// WRONG! }else { }
// Okay } else { }
-
開き中かっこ(
{
)の前。// WRONG! if (list.isEmpty()){ }
// Okay if (list.isEmpty()) { }
-
バイナリ演算子の両側。
// WRONG! val two = 1+1
// Okay val two = 1 + 1
これは、次のような「演算子に似た」記号にも適用されます。 <ph type="x-smartling-placeholder">- </ph>
- ラムダ式の矢印(
->
)。// WRONG! ints.map { value->value.toString() }
// Okay ints.map { value -> value.toString() }
-
メンバー参照の 2 つのコロン(
::
)。// WRONG! val toString = Any :: toString
// Okay val toString = Any::toString
-
ドット区切り文字(
.
)。// WRONG it . toString()
// Okay it.toString()
-
範囲演算子(
..
)。// WRONG for (i in 1 .. 4) { print(i) }
// Okay for (i in 1..4) { print(i) }
- ラムダ式の矢印(
-
コロン(
:
)の前。ただし、基本クラスまたはインターフェースを指定する目的でクラス宣言で使用する場合、またはwhere
句内で一般的な制約のために使用する場合に限る。// WRONG! class Foo: Runnable
// Okay class Foo : Runnable
// WRONG fun <T: Comparable> max(a: T, b: T)
// Okay fun <T : Comparable> max(a: T, b: T)
// WRONG fun <T> max(a: T, b: T) where T: Comparable<T>
// Okay fun <T> max(a: T, b: T) where T : Comparable<T>
-
カンマ(
,
)またはコロン(:
)の後。// WRONG! val oneAndTwo = listOf(1,2)
// Okay val oneAndTwo = listOf(1, 2)
// WRONG! class Foo :Runnable
// Okay class Foo : Runnable
-
行末コメント先頭の二重スラッシュ(
//
)の両側。ここで複数のスペースを使用することはできますが、必須ではありません。// WRONG! var debugging = false//disabled by default
// Okay var debugging = false // disabled by default
このルールは、行の先頭または末尾への追加スペースを必須または禁止とするものではなく、内部のスペースにのみ適用されます。
特定の構造
enum クラス
定数上に関数およびドキュメントのない enum は、1 行で記述することもできます。
enum class Answer { YES, NO, MAYBE }
enum の定数が別の行にある場合、定数間に空白行は必要ありません。ただし、その定数が本文を定義する場合を除きます。
enum class Answer { YES, NO, MAYBE { override fun toString() = """¯\_(ツ)_/¯""" } }
enum クラスはクラスであるため、クラスの形式を定めるその他すべてのルールが適用されます。
アノテーション
メンバーまたは型のアノテーションは、アノテーションが付加された構造の直前にある個別行に配置します。
@Retention(SOURCE) @Target(FUNCTION, PROPERTY_SETTER, FIELD) annotation class Global
引数のないアノテーションは 1 行に配置できます。
@JvmField @Volatile var disposable: Disposable? = null
引数のないアノテーションが 1 つだけ存在する場合、宣言と同じ行に配置できます。
@Volatile var disposable: Disposable? = null @Test fun selectAll() { // … }
@[...]
構文は、use-site target を明示的に指定する場合のみ使用できます。また、1 行に引数のない 2 つ以上のアノテーションを組み合わせる場合にのみ使用できます。
@field:[JvmStatic Volatile] var disposable: Disposable? = null
暗黙の戻り値 / プロパティ型
式関数本文またはプロパティ イニシャライザがスカラーである場合、値または戻り値の型は本文から明確に推定できるので、省略可能です。
override fun toString(): String = "Hey" // becomes override fun toString() = "Hey"
private val ICON: Icon = IconLoader.getIcon("/icons/kotlin.png") // becomes private val ICON = IconLoader.getIcon("/icons/kotlin.png")
ライブラリを記述する際、パブリック API の一部である場合は、明示的な型宣言を保持します。
命名
識別子には、ASCII 文字と数字のみを使用します。以下に述べる少数の例では、アンダースコアも使用されます。したがって、有効なそれぞれの識別子名は、正規表現 \w+
で照合されます。
name_
、mName
、s_name
、kName
の例に見られるような特殊な接頭辞または接尾辞は、バッキング プロパティの場合以外では使用されません(バッキング プロパティを参照)。
パッケージ名
パッケージ名では、連続した単語がアンダースコアなしで単純に連結され、すべて小文字となります。
// Okay package com.example.deepspace // WRONG! package com.example.deepSpace // WRONG! package com.example.deep_space
型名
クラス名は PascalCase で記述され、通常は名詞または名詞句を使用します。たとえば、Character
や ImmutableList
などです。インタフェース名にも名詞または名詞句を使用できます(例: List
)。ただし、場合によっては形容詞または形容詞句となることもあります(例: Readable
)。
テストクラスの名前は、テスト対象のクラスの名前で始まり、Test
で終わります。たとえば、HashTest
や HashIntegrationTest
などです。
関数名
関数名は camelCase で記述され、通常は動詞または動詞句を使用します。たとえば、sendMessage
や stop
などです。
テスト関数名では、名前の論理的構成要素の区切りとしてアンダースコアを使用できます。
@Test fun pop_emptyStack() { // … }
Unit
を返す @Composable
アノテーション付きの関数は、PascalCased であり、名前は型のような名詞として指定されます。
@Composable fun NameTag(name: String) { // … }
関数名にスペースを含めないでください。すべてのプラットフォームでサポートされているわけではないためです(特に、Android では完全にはサポートされていません)。
// WRONG! fun `test every possible case`() {} // OK fun testEveryPossibleCase() {}
定数名
定数名では UPPER_SNAKE_CASE を使用します。つまり、すべて大文字で、単語がアンダースコアで区切られます。しかし、そもそも定数とは何なのでしょうか。
定数とは、カスタムの get
関数のない val
プロパティです。その内容が変わることはなく、その関数には、目に見える副作用はありません。これには、変更不能型および変更不能型の変更不能コレクションに加えて、const
とマークされているスカラーおよび文字列が含まれます。観察できる状態のインスタンスが変更可能である場合、それは定数ではありません。オブジェクトを絶対に変えないと想定するだけでは不十分です。
const val NUMBER = 5 val NAMES = listOf("Alice", "Bob") val AGES = mapOf("Alice" to 35, "Bob" to 32) val COMMA_JOINER = Joiner.on(',') // Joiner is immutable val EMPTY_ARRAY = arrayOf()
これらの名前は通常、名詞または名詞句となります。
定数値は、object
の内部で、またはトップレベルの宣言としてのみ宣言できます。それ以外の点で定数の要件は満たしているものの class
の内部で定義されている値には、非定数名を使用する必要があります。
スカラー値である定数には const
修飾子を使用する必要があります。
非定数名
非定数名は camelCase で記述されます。これらは、インスタンス プロパティ、ローカル プロパティ、パラメータ名に適用されます。
val variable = "var" val nonConstScalar = "non-const" val mutableCollection: MutableSet= HashSet() val mutableElements = listOf(mutableInstance) val mutableValues = mapOf("Alice" to mutableInstance, "Bob" to mutableInstance2) val logger = Logger.getLogger(MyClass::class.java.name) val nonEmptyArray = arrayOf("these", "can", "change")
これらの名前は通常、名詞または名詞句となります。
バッキング プロパティ
バッキング プロパティが必要な場合、その名前は、実プロパティと完全一致する名前の前にアンダースコアを付けたものにする必要があります。
private var _table: Map? = null val table: Map get() { if (_table == null) { _table = HashMap() } return _table ?: throw AssertionError() }
型変数の名前
それぞれの型変数の名前は、次の 2 つのうちいずれかのスタイルに従います。
- 大文字 1 文字。任意でその後に数字 1 文字を続ける(
E
、T
、X
、T2
など) - クラスに使用される形式の名前の後に大文字
T
を続ける(RequestT
、FooBarT
など)
キャメルケース
たとえば「IPv6」や「iOS」のような頭字語または一般的でない構造が存在するときなど、場合によっては、合理的な数種類の方法で、英語のフレーズをキャメルケースに変換できます。予測性を改善するために、次のスキームを使用します。
散文的な形式の名前から始めます。
- フレーズをプレーン ASCII に変換し、アポストロフィを削除します。たとえば、「Müller’s algorithm」(Müller のアルゴリズム)は「Muellers algorithm」になります。
- この結果を単語に分割し、スペースやその他の句読点(通常はハイフン)で分けます。推奨: 一般的な用法ですでに伝統型キャメルケースのようになっている単語が含まれる場合は、構成パーツに分割します(例: 「AdWords」を「ad words」とする)。「iOS」などの単語は、実際には本質的なキャメルケースではありません。どの規則にも従っていないため、この推奨は適用されません。
- 次に、頭字語も含めてすべて小文字にし、次のいずれかを行います。
- 各単語の 1 文字目を大文字にしてパスカルケースを生成します。
- 最初の単語以外の各単語の 1 文字目を大文字にしてキャメルケースを生成します。
- 最後に、すべての単語を結合して 1 つの識別子にします。
元の単語の大文字小文字の区別は、ほぼ完全に無視されることにご注意ください。
簡潔な形式 | 正 | 誤 |
---|---|---|
「XML Http Request」(XML HTTP リクエスト) | XmlHttpRequest |
XMLHTTPRequest |
「new customer ID」(新規お客様 ID) | newCustomerId |
newCustomerID |
「inner stopwatch」(内側のストップウォッチ) | innerStopwatch |
innerStopWatch |
「supports IPv6 on iOS」(iOS で IPv6 をサポート) | supportsIpv6OnIos |
supportsIPv6OnIOS |
「YouTube importer」(YouTube インポート ツール) | YouTubeImporter |
YoutubeImporter * |
(* 使用できますが、推奨されません。)
ドキュメント
形式
KDoc ブロックの基本的な形式は、次の例のようになります。
/** * Multiple lines of KDoc text are written here, * wrapped normally… */ fun method(arg: String) { // … }
...または、この 1 行の例のようになります。
/** An especially short bit of KDoc. */
基本的な形式は常に許容されます。KDoc ブロック全体(コメント マーカーを含む)が 1 行に収まる場合は、1 行形式を代用できます。これは、@return
などのブロックタグがない場合のみ適用されます。
段落
各段落の間には、空白行 1 行、つまり、行揃えされた先頭アスタリスク(*
)のみを含む行が表示されます。また、ブロックタグのグループが存在する場合、その前にも表示されます。
ブロックタグ
使用される標準の「ブロックタグ」は、@constructor
、@receiver
、@param
、@property
、@return
、@throws
、@see
の順番で表示されます。これらに付随する説明が空白になることはありません。
ブロックタグが 1 行に収まらない場合は、継続する行が @
の位置からスペース 4 つ分インデントされます。
サマリー フラグメント
各 KDoc ブロックは、簡単なサマリー フラグメントから始まります。このフラグメントは非常に重要です。クラスやメソッドのインデックスといった特定のコンテキストでは、このテキスト部分のみが表示されます。
これはフラグメントです。名詞句または動詞句であり、完全な文ではありません。フラグメントは、「A `Foo` is a...
」または「This method returns...
」で始まるわけではなく、「Save the record.
」のような完全な命令文を形成する必要もありません。ただし、完全な文と同様の大文字と句読点が使用されます。
使用方法
KDoc は少なくとも、すべての public
型、およびその型のすべての public
または protected
メンバーに存在します。ただし、次のように、いくつかの例外があります。
例外: 名前どおりで説明を要しない関数
getFoo
のような「シンプルでわかりやすい」関数および foo
のようなプロパティについて、「Foo を返す」以外には実際になんの説明も必要としないケースでは、KDoc は省略可能です。
この例外を口実にして、一般的な読者が知っておくべき関連情報を省略するのは、適切なことではありません。たとえば、getCanonicalName
という名前の関数または canonicalName
という名前のプロパティについて、「canonical name」(規範的な名前)という用語の意味が一般的読者にはわからない可能性がある場合は、(/** Returns the canonical name. */
としか言っていないので)そのドキュメントを省略しないでください。
例外: オーバーライド
スーパータイプ メソッドをオーバーライドするメソッドでは KDoc が存在するとは限りません。