6 月 3 日の「#Android11: The Beta Launch Show」にぜひご参加ください。

Kotlin スタイルガイド

このドキュメントは、Kotlin プログラミング言語でのソースコードの記述について Google が推奨する Android コーディング標準の定義を完全に網羅しています。Kotlin ソースファイルを Google Android スタイルとして記述するためには、必ずここに記載されているルールに従う必要があります。

他のプログラミング スタイルガイドと同様に、読みやすいフォーマットだけでなく、他の種類の規則やコーディング標準も扱っています。ただし、このドキュメントでは、主として絶対に守るべき厳格なルールに焦点を絞り、(手動であれツールによってであれ)明らかに適用すべきとはいえない助言は行いません。

最終更新日: 2018-05-18

ソースファイル

ソースファイルはすべて 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> = // …
    

特殊文字

空白文字

行末シーケンスを除き、ソースファイルに使用できる空白文字は 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 if/else ブランチがなく 1 行に収まる if ステートメント本文および when ブランチには、中かっこは必須ではありません。

    if (string.isEmpty()) return

    when (value) {
        0 -> return
        // …
    }
    

それ以外は、本文が空白またはステートメントを 1 つしか含まない場合でも、ifforwhen ブランチ、dowhile ステートメントには中かっこが必要です。

    if (string.isEmpty())
        return  // WRONG!

    if (string.isEmpty()) {
        return  // Okay
    }
    

空白でないブロック

空白でないブロックおよびブロック状構造の中かっこは、次のように 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 ステートメント
  • シェルに切り取り&貼り付けされる可能性のあるコメント内コマンドライン

改行箇所

行の折り返しに関する主要な指示: より高い構文レベルでの改行を優先。別の指示:

  • 非割り当て演算子の箇所で改行される場合、改行は記号の前に入ります。
    • 次のような「演算子に似た」記号にも適用されます。
    • ドット区切り文字(.)。
    • メンバー参照の 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"
    

式関数が複数の行に折り返されるのは、ブロックを開くときだけです。

    fun main() = runBlocking {
      // …
    }
    

それ以外では、折り返しが必要になるほど式関数が長くなる場合には、代わりに、通常の関数本文、return 宣言、通常の式折り返しルールを使用します。

プロパティ

プロパティ イニシャライザが 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 文字も使用されます。

  • ifforcatch などの予約語を、行の上でその後ろにある左かっこ(()と区切る箇所。
        // 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
        
    次のような「演算子に似た」記号にも適用されます。
    • ラムダ式の矢印(->)。
          // 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() {
        // …
    }
    

暗黙の戻り値 / プロパティ型

式関数本文またはプロパティ イニシャライザがスカラーである場合、値または戻り値の型は本文から明確に推定できるので、省略可能です。

    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_mNames_namekName の例に見られるような特殊な接頭辞または接尾辞は、バッキング プロパティの場合以外では使用されません(バッキング プロパティを参照)。

パッケージ名

パッケージ名では、連続した単語がアンダースコアなしで単純に連結され、すべて小文字となります。

    // Okay
    package com.example.deepspace
    // WRONG!
    package com.example.deepSpace
    // WRONG!
    package com.example.deep_space
    

型の名前

クラス名は PascalCase で記述され、通常は名詞または名詞句を使用します。たとえば、CharacterImmutableList などです。インタフェース名にも名詞または名詞句を使用できます(例: List)。ただし、場合によっては形容詞または形容詞句となることもあります(例: Readable)。

テストクラスの名前は、テスト対象のクラスの名前で始まり、Test で終わります。たとえば、HashTestHashIntegrationTest などです。

関数名

関数名は camelCase で記述され、通常は動詞または動詞句を使用します。たとえば、sendMessagestop などです。

テスト関数名では、名前の論理的構成要素の区切りとしてアンダースコアを使用できます。

    @Test fun pop_emptyStack() {
        // …
    }
    

定数名

定数名では 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 文字を続ける(ETXT2 など)
  • クラスに使用される形式の名前の後に大文字 T を続ける(RequestTFooBarT など)

キャメルケース

たとえば「IPv6」や「iOS」のような頭字語または一般的でない構造が存在するときなど、場合によっては、合理的な数種類の方法で、英語のフレーズをキャメルケースに変換できます。予測性を改善するために、次のスキームを使用します。

最初に簡潔な形式の名前を用意します。

  1. フレーズをプレーン ASCII に変換し、アポストロフィを削除します。 たとえば、「Müller’s algorithm」(Müller のアルゴリズム)は「Muellers algorithm」になります。
  2. この結果を単語に分割し、スペースやその他の句読点(通常はハイフン)で分けます。 推奨: 一般的な用法ですでに伝統型キャメルケースのようになっている単語が含まれる場合は、構成パーツに分割します(例: 「AdWords」を「ad words」とする)。「iOS」などの単語は、実際には本質的なキャメルケースではありません。どの規則にも従っていないため、この推奨は適用されません。
  3. 次に、頭字語も含めてすべて小文字にし、次のいずれかを行います。
    • 各単語の 1 文字目を大文字にしてパスカルケースを生成します。
    • 最初の単語以外の各単語の 1 文字目を大文字にしてキャメルケースを生成します。
  4. 最後に、すべての単語を結合して 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 が存在するとは限りません。