Kotlin 生成コードガイド
proto2、proto3、およびエディションで生成されたコードの違いが強調されています。これらの違いは、このドキュメントで説明されている生成されたコードにあり、両方のバージョンで同じである基本メッセージクラス/インターフェースにはないことに注意してください。このドキュメントを読む前に、proto2 言語ガイド、proto3 言語ガイド、および/またはエディションガイドを読む必要があります。
コンパイラの呼び出し
プロトコルバッファコンパイラは、Java コードの上に構築される Kotlin コードを生成します。その結果、--java_out= と --kotlin_out= の 2 つのコマンドラインフラグを付けて呼び出す必要があります。--java_out= オプションのパラメータは、コンパイラが Java 出力を書き込むディレクトリであり、--kotlin_out= も同様です。入力された各 .proto ファイルについて、コンパイラは .proto ファイル自体を表す Java クラスを含むラッパー .java ファイルを作成します。
.proto ファイルに次の行が含まれているかどうかにかかわらず
option java_multiple_files = true;
コンパイラは、.proto ファイルで宣言された各トップレベルメッセージに対して生成する各クラスとファクトリメソッドに対して個別の .kt ファイルを作成します。
各ファイルの Java パッケージ名は、Java 生成コードリファレンスで説明されている生成された Java コードによって使用されるものと同じです。
出力ファイルは、--kotlin_out= のパラメータ、パッケージ名(ピリオド [.] がスラッシュ [/] に置き換えられたもの)、およびサフィックス Kt.kt ファイル名を連結することで選択されます。
例えば、次のようにコンパイラを呼び出すとします。
protoc --proto_path=src --java_out=build/gen/java --kotlin_out=build/gen/kotlin src/foo.proto
foo.proto の Java パッケージが com.example で、Bar という名前のメッセージが含まれている場合、プロトコルバッファコンパイラはファイル build/gen/kotlin/com/example/BarKt.kt を生成します。プロトコルバッファコンパイラは、必要に応じて build/gen/kotlin/com および build/gen/kotlin/com/example ディレクトリを自動的に作成します。ただし、build/gen/kotlin、build/gen、または build は作成しません。これらは既に存在している必要があります。単一の呼び出しで複数の .proto ファイルを指定できます。すべての出力ファイルは一度に生成されます。
メッセージ
単純なメッセージ宣言を考えます。
message FooBar {}
プロトコルバッファコンパイラは、生成された Java コードに加えて、FooBarKt という名前のオブジェクトと、次の構造を持つ 2 つのトップレベル関数を生成します。
object FooBarKt {
class Dsl private constructor { ... }
}
inline fun fooBar(block: FooBarKt.Dsl.() -> Unit): FooBar
inline fun FooBar.copy(block: FooBarKt.Dsl.() -> Unit): FooBar
ネストされた型
メッセージは別のメッセージ内で宣言できます。例:
message Foo {
message Bar { }
}
この場合、コンパイラは BarKt オブジェクトと bar ファクトリメソッドを FooKt の中にネストしますが、copy メソッドはトップレベルのままです。
object FooKt {
class Dsl { ... }
object BarKt {
class Dsl private constructor { ... }
}
inline fun bar(block: FooKt.BarKt.Dsl.() -> Unit): Foo.Bar
}
inline fun foo(block: FooKt.Dsl.() -> Unit): Foo
inline fun Foo.copy(block: FooKt.Dsl.() -> Unit): Foo
inline fun Foo.Bar.copy(block: FooKt.BarKt.Dsl.() -> Unit): Foo.Bar
フィールド
前のセクションで説明したメソッドに加えて、プロトコルバッファコンパイラは、.proto ファイルのメッセージ内で定義された各フィールドに対して、DSL に可変プロパティを生成します。(Kotlin は、Java によって生成されたゲッターからメッセージオブジェクトの読み取り専用プロパティを既に推論しています。)
プロパティは、.proto ファイルのフィールド名がアンダースコア付きの小文字を使用している場合でも(そうあるべきですが)、常にキャメルケースの命名を使用することに注意してください。ケース変換は次のように機能します。
- 名前の各アンダースコアについて、アンダースコアは削除され、次の文字が大文字になります。
- 名前がプレフィックス(たとえば「clear」)を持つ場合、最初の文字は大文字になります。それ以外の場合は小文字になります。
したがって、フィールド foo_bar_baz は fooBarBaz になります。
Kotlin の予約語や Protobuf ライブラリに既に定義されているメソッドとフィールド名が競合するいくつかの特別なケースでは、追加のアンダースコアが追加されます。たとえば、in という名前のフィールドのクリアラーは clearIn_() です。
単数フィールド
このフィールド定義について:
int32 foo = 1;
コンパイラは、DSL に次のアクセサーを生成します。
fun hasFoo(): Boolean: フィールドが設定されている場合はtrueを返します。これは暗黙的な存在を使用するフィールドには生成されません。var foo: Int: フィールドの現在の値。フィールドが設定されていない場合は、デフォルト値を返します。fun clearFoo(): フィールドの値をクリアします。これを呼び出した後、hasFoo()はfalseを返し、getFoo()はデフォルト値を返します。
その他の単純なフィールドタイプの場合、対応する Java タイプはスカラー値型テーブルに従って選択されます。メッセージ型と enum 型の場合、値型はメッセージまたは enum クラスに置き換えられます。メッセージ型は依然として Java で定義されているため、メッセージ内の符号なし型は、Java および古いバージョンの Kotlin との互換性のために、DSL で標準の対応する符号付き型を使用して表されます。
埋め込みメッセージフィールド
サブメッセージの特別な処理はないことに注意してください。たとえば、次のようなフィールドがある場合
optional Foo my_foo = 1;
次のように記述する必要があります
myFoo = foo {
...
}
一般的に、これはコンパイラが Foo に Kotlin DSL があるかどうか、またはたとえば Java API のみが生成されているかどうかを知らないためです。これは、依存するメッセージが Kotlin コード生成を追加するのを待つ必要がないことを意味します。
繰り返しフィールド
このフィールド定義について:
repeated string foo = 1;
コンパイラは、DSL に次のメンバーを生成します。
class FooProxy: DslProxy, 総称型でのみ使用される構築不可能な型val fooList: DslList<String, FooProxy>, 繰り返しフィールドの現在の要素のリストの読み取り専用ビューfun DslList<String, FooProxy>.add(value: String), 繰り返しフィールドに要素を追加できる拡張関数operator fun DslList<String, FooProxy>.plusAssign(value: String),addのエイリアスfun DslList<String, FooProxy>.addAll(values: Iterable<String>), 繰り返しフィールドに要素のIterableを追加できる拡張関数operator fun DslList<String, FooProxy>.plusAssign(values: Iterable<String>),addAllのエイリアスoperator fun DslList<String, FooProxy>.set(index: Int, value: String), 指定されたゼロベースのインデックスの要素の値を設定する拡張関数fun DslList<String, FooProxy>.clear(), 繰り返しフィールドの内容をクリアする拡張関数
この珍しい構造により、fooList は DSL のスコープ内で可変リストのように「振る舞い」、基になるビルダーでサポートされているメソッドのみをサポートしながら、DSL からの可変性が「漏れ出す」のを防ぎ、混乱を招く副作用を引き起こす可能性があります。
その他の単純なフィールドタイプの場合、対応する Java タイプはスカラー値型テーブルに従って選択されます。メッセージ型と enum 型の場合、タイプはメッセージまたは enum クラスです。
Oneof フィールド
この oneof フィールド定義の場合
oneof oneof_name {
int32 foo = 1;
...
}
コンパイラは、DSL に次のアクセサーメソッドを生成します。
val oneofNameCase: OneofNameCase:oneof_nameフィールドのいずれかが設定されているかどうかを取得します。戻り値の型については、Java コードリファレンスを参照してください。fun hasFoo(): Boolean: oneof ケースがFOOの場合、trueを返します。val foo: Int: oneof ケースがFOOの場合、oneof_nameの現在の値を返します。それ以外の場合は、このフィールドのデフォルト値を返します。
その他の単純なフィールドタイプの場合、対応する Java タイプはスカラー値型テーブルに従って選択されます。メッセージ型と enum 型の場合、値型はメッセージまたは enum クラスに置き換えられます。
マップフィールド
このmapフィールド定義の場合:
map<int32, int32> weight = 1;
コンパイラは、DSL クラスに次のメンバーを生成します。
class WeightProxy private constructor(): DslProxy(), 総称型でのみ使用される構築不可能な型val weight: DslMap<Int, Int, WeightProxy>, マップフィールドの現在のエントリの読み取り専用ビューfun DslMap<Int, Int, WeightProxy>.put(key: Int, value: Int): エントリをこのマップフィールドに追加しますoperator fun DslMap<Int, Int, WeightProxy>.put(key: Int, value: Int): 演算子構文を使用したputのエイリアスfun DslMap<Int, Int, WeightProxy>.remove(key: Int):keyに関連付けられたエントリが存在する場合に削除しますfun DslMap<Int, Int, WeightProxy>.putAll(map: Map<Int, Int>): 指定されたマップのすべてのエントリをこのマップフィールドに追加し、既に存在するキーの以前の値を上書きしますfun DslMap<Int, Int, WeightProxy>.clear(): このマップフィールドからすべてのエントリをクリアします
エクステンション
拡張範囲を持つ proto2 またはエディションのメッセージが与えられた場合
message Foo {
extensions 100 to 199;
}
プロトコルバッファコンパイラは、FooKt.Dsl に次のメソッドを追加します。
operator fun <T> get(extension: ExtensionLite<Foo, T>): T: DSL の拡張フィールドの現在の値を取得しますoperator fun <T> get(extension: ExtensionLite<Foo, List<T>>): ExtensionList<T, Foo>: DSL の繰り返し拡張フィールドの現在の値を読み取り専用のListとして取得しますoperator fun <T : Comparable<T>> set(extension: ExtensionLite<Foo, T>): DSL の拡張フィールドの現在の値を設定します(Comparableフィールドタイプの場合)operator fun <T : MessageLite> set(extension: ExtensionLite<Foo, T>): DSL の拡張フィールドの現在の値を設定します(メッセージフィールドタイプの場合)operator fun set(extension: ExtensionLite<Foo, ByteString>): DSL の拡張フィールドの現在の値を設定します(bytesフィールドの場合)operator fun contains(extension: ExtensionLite<Foo, *>): Boolean: 拡張フィールドに値がある場合、true を返しますfun clear(extension: ExtensionLite<Foo, *>): 拡張フィールドをクリアしますfun <E> ExtensionList<Foo, E>.add(value: E): 繰り返し拡張フィールドに値を追加しますoperator fun <E> ExtensionList<Foo, E>.plusAssign(value: E): 演算子構文を使用したaddのエイリアスoperator fun <E> ExtensionList<Foo, E>.addAll(values: Iterable<E>): 繰り返し拡張フィールドに複数の値を追加しますoperator fun <E> ExtensionList<Foo, E>.plusAssign(values: Iterable<E>): 演算子構文を使用したaddAllのエイリアスoperator fun <E> ExtensionList<Foo, E>.set(index: Int, value: E): 指定されたインデックスの繰り返し拡張フィールドの要素を設定しますinline fun ExtensionList<Foo, *>.clear(): 繰り返し拡張フィールドの要素をクリアします
ここでの総称型は複雑ですが、this[extension] = value は繰り返し拡張を除くすべての拡張タイプで機能し、繰り返し拡張には非拡張繰り返しフィールドと同様に機能する「自然な」リスト構文があるという効果があります。
エクステンション定義が与えられた場合:
extend Foo {
int32 bar = 123;
}
Java は、上記の拡張操作を「キー」として使用される「拡張識別子」bar を生成します。