web-dev-qa-db-ja.com

セミコロン推論のルールは何ですか?

Kotlinは「セミコロン推論」を提供します。構文的には、サブセンテンス(ステートメント、宣言など)は、「セミコロンまたは改行」を表す疑似トークンSEMIによって分離されます。ほとんどの場合、Kotlinコードにセミコロンは必要ありません。

これは 文法 ページが言っていることです。これは、場合によってはセミコロンを指定する必要があることを意味しているようですが、セミコロンを指定しておらず、以下の文法ツリーではこれが明確に示されていません。また、この機能が正しく動作せず、問題が発生する場合もあるのではないかと思います。

したがって、問題は、セミコロンをいつ挿入する必要があるのか​​、誤ったコードを記述しないようにするために知っておく必要のあるコーナーケースは何かということです。

12
Malcolm

セミコロンを指定する必要があるのは、コンパイラーが実行しようとしていることがあいまいな場合のみです。セミコロンがないと、明らかなコンパイラエラーが発生します。

ルールは次のとおりです:これについて心配する必要はなく、セミコロンをまったく使用しないでください(以下の2つの場合を除く)。コンパイラは、間違いがあった場合に通知します。誤って余分なセミコロンを追加した場合でも、構文の強調表示により、「冗長セミコロン」の警告とともに不要であることが示されます。

セミコロンの2つの一般的なケース:

列挙型のリストと、列挙型のプロパティまたは関数を持つ列挙型クラスには、列挙型リストの後に_;_が必要です。次に例を示します。

_enum class Things {
    ONE, TWO;

    fun isOne(): Boolean = this == ONE
}
_

そしてこの場合、コンパイラはあなたがそれを正しく行わなかった場合に直接あなたに知らせます:

エラー:(y、x)Kotlin:「;」が必要です最後の列挙型エントリの後、または列挙型クラス本体を閉じるには「}」

それ以外の場合、他の一般的なケースは、簡潔にするために、同じ行で2つのステートメントを実行している場合のみです。

_myThingMap.forEach { val (key, value) = it; println("mapped $key to $value") } 
_

この最後の例にセミコロンがない場合、何をしているのか混乱する時点で、より不思議なエラーが発生します。セミコロンで区切られた2つのステートメントとして両方とも有効であり、セミコロンが削除されて1つになったときにも有効なコードを作成するのは非常に困難です。

過去には、Kotlin 1.0より前は_{ ... }_であり、後に_init { ... }_になったクラスの初期化ブロックのような他のケースがありましたが、セミコロンがはるかに明確であるため、セミコロンは不要になりました。これらのケースはもはや言語に残っていません。

この機能への信頼:

また、この機能が正しく動作せず、問題が発生する場合があるのではないかと疑っています。

この機能はうまく機能し、この機能に問題があるという証拠はどこにもありません。また、Kotlinの長年の経験から、この機能が逆効果となる既知のケースは見つかりませんでした。 _;_の欠落に問題がある場合、コンパイラーはエラーを報告します。

私のオープンソースのKotlinと、社内のかなり大規模なKotlinプロジェクトをすべて検索したところ、上記の場合以外にセミコロンは見つかりませんでした。合計でごくわずかです。原則として「Kotlinではセミコロンを使用しない」という概念をサポートします。

ただし、セミコロンの有無にかかわらず、有効で意味が異なるコードを作成したために、コンパイラーがエラーを報告しない場合を意図的に考案できる可能性があります。これは次のようになります(@Ruckusによる回答の修正版):

_fun whatever(msg: String, optionalFun: ()->Unit = {}): () -> Unit = ...

val doStuff: () -> Unit = when(x) {
    is String -> {
        { doStuff(x) }
    }
    else -> { 
        whatever("message") // absence or presence of semicolon changes behavior
        { doNothing() }
    }
}
_

この場合、doStuffには、タイプ_()->Unit_の関数であるwhatever("message") { doNothing() }への呼び出しの結果が割り当てられます。セミコロンを追加すると、関数{ doNothing() }が割り当てられます。これもタイプ_()->Unit_です。したがって、コードは両方の方法で有効です。 しかし、すべてが完全に整列している必要があるため、このようなことが自然に発生するのを見たことがありません機能はemitキーワードまたは_^_ハット演算子を提案しました はこのケースを不可能にし、意見と時間の制約が強く反対されたため、1.0より前に検討されました。

18
Jayson Minard

Jayson Minardの答えに加えて、セミコロンが必要なもう1つの奇妙なEdgeケースに遭遇しました。 returnステートメントを使用せずに関数を返すステートメントブロックを使用している場合は、セミコロンが必要です。例えば:

_val doStuff: () -> Unit = when(x) {
    is String -> {
        { doStuff(x) }
    }
    else -> { 
        println("This is the alternate");  // Semicolon needed here
        { doNothing() }
    }
}
_

セミコロンがない場合、Kotlinは{ doNothing() }ステートメントがprintln()の2番目の引数であると見なし、コンパイラーがエラーを報告します。

8
Ruckus T-Boom