Kotlin生成コードガイド
proto2、proto3、およびeditionsで生成されたコード間の違いは強調表示されています。これらの違いは、このドキュメントで説明されている生成されたコードにあり、両方のバージョンで同じである基本メッセージクラス/インターフェースにはないことに注意してください。このドキュメントを読む前に、proto2言語ガイド、proto3言語ガイド、および/またはEditionsガイドを読む必要があります。
コンパイラの呼び出し
protocol bufferコンパイラは、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
という名前のメッセージが含まれている場合、protocol bufferコンパイラはファイルbuild/gen/kotlin/com/example/BarKt.kt
を生成します。protocol bufferコンパイラは、必要に応じてbuild/gen/kotlin/com
およびbuild/gen/kotlin/com/example
ディレクトリを自動的に作成します。ただし、build/gen/kotlin
、build/gen
、またはbuild
は作成しません。これらは既に存在している必要があります。1回の呼び出しで複数の.proto
ファイルを指定できます。すべての出力ファイルが一度に生成されます。
メッセージ
単純なメッセージ宣言を考えます。
message FooBar {}
protocol bufferコンパイラは、生成された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
フィールド
前のセクションで説明したメソッドに加えて、protocol bufferコンパイラは、.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>
、repeatedフィールド内の現在の要素のリストの読み取り専用ビューfun DslList<String, FooProxy>.add(value: String)
、repeatedフィールドに要素を追加できる拡張関数operator fun DslList<String, FooProxy>.plusAssign(value: String)
、add
のエイリアスfun DslList<String, FooProxy>.addAll(values: Iterable<String>)
、repeatedフィールドに要素の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()
、repeatedフィールドの内容をクリアする拡張関数
この珍しい構造により、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>
、mapフィールド内の現在のエントリの読み取り専用ビューfun DslMap<Int, Int, WeightProxy>.put(key: Int, value: Int)
: このmapフィールドにエントリを追加します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>)
: 指定されたマップからすべてのエントリをこのmapフィールドに追加し、既に存在するキーの以前の値を上書きしますfun DslMap<Int, Int, WeightProxy>.clear()
: このmapフィールドからすべてのエントリをクリアします
拡張
拡張範囲を持つproto2またはeditionsメッセージが与えられた場合:
message Foo {
extensions 100 to 199;
}
protocol bufferコンパイラは、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内のrepeated拡張フィールドの現在の値を読み取り専用の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)
: repeated拡張フィールドに値を追加しますoperator fun <E> ExtensionList<Foo, E>.plusAssign(value: E)
: 演算子構文を使用したadd
のエイリアスoperator fun <E> ExtensionList<Foo, E>.addAll(values: Iterable<E>)
: repeated拡張フィールドに複数の値を追加しますoperator fun <E> ExtensionList<Foo, E>.plusAssign(values: Iterable<E>)
: 演算子構文を使用したaddAll
のエイリアスoperator fun <E> ExtensionList<Foo, E>.set(index: Int, value: E)
: 指定されたインデックスのrepeated拡張フィールドの要素を設定しますinline fun ExtensionList<Foo, *>.clear()
: repeated拡張フィールドの要素をクリアします
ここでのジェネリクスは複雑ですが、その効果として、this[extension] = value
はrepeated拡張を除くすべての拡張型で機能し、repeated拡張は拡張でないrepeatedフィールドと同様に機能する「自然な」リスト構文を持ちます。
拡張定義が与えられた場合:
extend Foo {
int32 bar = 123;
}
Javaは「拡張識別子」bar
を生成します。これは上記の拡張操作を「キー」として使用されます。