Java生成コードガイド
proto2とproto3の生成コードの違いが強調されています。これらの違いは、このドキュメントで説明されている生成コードにあり、両バージョンで同じである基本メッセージクラス/インターフェースにはないことに注意してください。このドキュメントを読む前に、proto2言語ガイドおよび/またはproto3言語ガイドを読むことをお勧めします。
特に指定がない限り、Javaのプロトコルバッファメソッドはnullを受け入れたり返したりしないことに注意してください。
コンパイラの呼び出し
プロトコルバッファコンパイラは、--java_out=
コマンドラインフラグを付けて呼び出すとJava出力を生成します。--java_out=
オプションのパラメータは、コンパイラがJava出力を書き込むディレクトリです。各.proto
ファイル入力に対して、コンパイラは.proto
ファイル自体を表すJavaクラスを含むラッパー.java
ファイルを作成します。
もし.proto
ファイルが以下のような行を含んでいる場合
option java_multiple_files = true;
その場合、コンパイラは、各トップレベルのメッセージ、列挙型、および.proto
ファイルで宣言されたサービスに対して生成するクラス/列挙型ごとに、個別の.java
ファイルも作成します。
それ以外の場合(デフォルトであるjava_multiple_files
オプションが無効になっている場合)、前述のラッパークラスはアウタークラスとしても使用され、.proto
ファイルで宣言された各トップレベルのメッセージ、列挙型、およびサービスに対して生成されるクラス/列挙型はすべて、アウターラッパークラス内にネストされます。したがって、コンパイラは.proto
ファイル全体に対して単一の.java
ファイルのみを生成し、パッケージ内に余分なレイヤーを持つことになります
ラッパークラスの名前は次のように選択されます。もし.proto
ファイルが以下のような行を含んでいる場合
option java_outer_classname = "Foo";
その場合、ラッパークラスの名前はFoo
になります。それ以外の場合、ラッパークラスの名前は.proto
ファイルのベース名をキャメルケースに変換することで決定されます。例えば、foo_bar.proto
はFooBar
というクラス名を生成します。もしファイル内に同じ名前のサービス、列挙型、またはメッセージ(ネストされた型を含む)がある場合、ラッパークラスの名前に「OuterClass」が追加されます。例
- もし
foo_bar.proto
にFooBar
というメッセージが含まれている場合、ラッパークラスはFooBarOuterClass
というクラス名を生成します。 - もし
foo_bar.proto
にFooService
というサービスが含まれており、かつjava_outer_classname
も文字列FooService
に設定されている場合、ラッパークラスはFooServiceOuterClass
というクラス名を生成します。
注
非推奨のprotobuf API v1を使用している場合、メッセージ名との衝突に関わらずOuterClass
が追加されます。ネストされたクラスに加えて、ラッパークラス自体は以下のAPIを持ちます(ラッパークラスがFoo
と命名され、foo.proto
から生成されたと仮定します)
public final class Foo {
private Foo() {} // Not instantiable.
/** Returns a FileDescriptor message describing the contents of {@code foo.proto}. */
public static com.google.protobuf.Descriptors.FileDescriptor getDescriptor();
/** Adds all extensions defined in {@code foo.proto} to the given registry. */
public static void registerAllExtensions(com.google.protobuf.ExtensionRegistry registry);
public static void registerAllExtensions(com.google.protobuf.ExtensionRegistryLite registry);
// (Nested classes omitted)
}
Javaパッケージ名は、以下のパッケージで説明されているように選択されます。
出力ファイルは、--java_out=
のパラメータ、パッケージ名(.
を/
に置換したもの)、および.java
ファイル名を連結して決定されます。
例えば、次のようにコンパイラを呼び出したとします
protoc --proto_path=src --java_out=build/gen src/foo.proto
もしfoo.proto
のJavaパッケージがcom.example
で、java_multiple_files
を有効にしておらず、アウタークラス名がFooProtos
の場合、プロトコルバッファコンパイラはファイルbuild/gen/com/example/FooProtos.java
を生成します。プロトコルバッファコンパイラは、必要に応じてbuild/gen/com
およびbuild/gen/com/example
ディレクトリを自動的に作成します。ただし、build/gen
やbuild
は作成されません。これらは既に存在している必要があります。単一の呼び出しで複数の.proto
ファイルを指定でき、すべての出力ファイルが一度に生成されます。
Javaコードを出力する際、プロトコルバッファコンパイラがJARアーカイブに直接出力できる機能は特に便利です。なぜなら、多くのJavaツールがJARファイルから直接ソースコードを読み取ることができるからです。JARファイルに出力するには、単に.jar
で終わる出力場所を指定するだけです。アーカイブにはJavaソースコードのみが配置されることに注意してください。Javaクラスファイルを生成するには、別途コンパイルする必要があります。
パッケージ
生成されたクラスは、java_package
オプションに基づいてJavaパッケージに配置されます。このオプションが省略された場合、代わりにpackage
宣言が使用されます。
例えば、もし.proto
ファイルが以下を含んでいる場合
package foo.bar;
その場合、結果のJavaクラスはJavaパッケージfoo.bar
に配置されます。しかし、もし.proto
ファイルが次のようにjava_package
オプションも含む場合
package foo.bar;
option java_package = "com.example.foo.bar";
その場合、クラスは代わりにcom.example.foo.bar
パッケージに配置されます。java_package
オプションは、通常の.proto
package
宣言が逆ドメイン名で始まることが想定されていないために提供されます。
メッセージ
新しいプロトコルバッファスキーマを設計する場合は、Javaのproto名の推奨事項を参照してください。
シンプルなメッセージ宣言が与えられた場合
message Foo {}
プロトコルバッファコンパイラは、Message
インターフェースを実装するFoo
というクラスを生成します。このクラスはfinal
として宣言されており、さらなるサブクラス化は許可されません。Foo
はGeneratedMessage
を継承しますが、これは実装の詳細とみなされるべきです。デフォルトでは、Foo
はGeneratedMessage
の多くのメソッドを、最大速度のための特殊なバージョンでオーバーライドします。ただし、もし.proto
ファイルが以下の行を含んでいる場合
option optimize_for = CODE_SIZE;
その場合、Foo
は機能に必要な最小限のメソッドのみをオーバーライドし、残りはGeneratedMessage
の反射ベースの実装に依存します。これにより、生成されるコードのサイズが大幅に削減されますが、パフォーマンスも低下します。あるいは、もし.proto
ファイルが以下を含んでいる場合
option optimize_for = LITE_RUNTIME;
その場合、Foo
はすべてのメソッドの高速な実装を含みますが、Message
のメソッドのサブセットを含むMessageLite
インターフェースを実装します。特に、記述子、ネストされたビルダー、またはリフレクションをサポートしません。しかし、このモードでは、生成されたコードはlibprotobuf.jar
ではなくlibprotobuf-lite.jar
にリンクするだけで済みます。「lite」ライブラリはフルライブラリよりもはるかに小さく、携帯電話などのリソースが制約されたシステムにより適しています。
Message
インターフェースは、メッセージ全体をチェック、操作、読み取り、または書き込みできるメソッドを定義します。これらのメソッドに加えて、Foo
クラスは以下の静的メソッドを定義します
static Foo getDefaultInstance()
:Foo
のシングルトンインスタンスを返します。このインスタンスの内容は、Foo.newBuilder().build()
を呼び出した場合と同一です(つまり、すべての単一フィールドは設定されておらず、すべての繰り返しフィールドは空です)。メッセージのデフォルトインスタンスは、newBuilderForType()
メソッドを呼び出すことでファクトリとして使用できることに注意してください。static Descriptor getDescriptor()
: 型の記述子を返します。これには、型に関する情報(どのようなフィールドを持ち、その型が何であるかなど)が含まれています。これは、Message
のリフレクションメソッド(例:getField()
)とともに使用できます。static Foo parseFrom(...)
: 指定されたソースからFoo
型のメッセージをパースし、それを返します。Message.Builder
インターフェースのmergeFrom()
の各バリアントに対応するparseFrom
メソッドが1つあります。parseFrom()
はUninitializedMessageException
をスローしないことに注意してください。パースされたメッセージに必須フィールドが不足している場合、InvalidProtocolBufferException
をスローします。これは、Foo.newBuilder().mergeFrom(...).build()
を呼び出す場合とは微妙に異なります。static Parser parser()
: 様々なparseFrom()
メソッドを実装するParser
のインスタンスを返します。Foo.Builder newBuilder()
: 新しいビルダーを作成します(以下で説明)。Foo.Builder newBuilder(Foo prototype)
:prototype
と同じ値ですべてのフィールドが初期化された新しいビルダーを作成します。埋め込みメッセージと文字列オブジェクトは不変であるため、オリジナルとコピー間で共有されます。
ビルダー
上記で説明したFoo
クラスのインスタンスのようなメッセージオブジェクトは、JavaのString
と同様に不変です。メッセージオブジェクトを構築するには、ビルダーを使用する必要があります。各メッセージクラスには独自のビルダークラスがあります。したがって、Foo
の例では、プロトコルバッファコンパイラはFoo
を構築するために使用できるネストされたクラスFoo.Builder
を生成します。Foo.Builder
はMessage.Builder
インターフェースを実装します。これはGeneratedMessage.Builder
クラスを継承しますが、これもまた実装の詳細とみなされるべきです。Foo
と同様に、Foo.Builder
はGeneratedMessage.Builder
の汎用メソッド実装に依存するか、optimize_for
オプションが使用される場合は、はるかに高速なカスタムコードを生成する場合があります。静的メソッドFoo.newBuilder()
を呼び出すことでFoo.Builder
を取得できます。
Foo.Builder
は静的メソッドを定義しません。そのインターフェースはMessage.Builder
インターフェースで定義されているものとまったく同じですが、戻り値の型がより具体的であるという例外があります。ビルダーを変更するFoo.Builder
のメソッドは型Foo.Builder
を返し、build()
は型Foo
を返します。
フィールドセッターを含むビルダーの内容を変更するメソッドは、常にビルダーへの参照を返します(つまり、「return this;
」を返します)。これにより、複数のメソッド呼び出しを1行でチェーンすることができます。例: builder.mergeFrom(obj).setFoo(1).setBar("abc").clearBaz();
ビルダーはスレッドセーフではないため、複数の異なるスレッドが単一のビルダーの内容を変更する必要がある場合は常にJavaの同期を使用する必要があることに注意してください。
サブビルダー
サブメッセージを含むメッセージの場合、コンパイラはサブビルダーも生成します。これにより、深くネストされたサブメッセージを再構築することなく、繰り返し変更することができます。例
message Foo {
optional int32 val = 1;
// some other fields.
}
message Bar {
optional Foo foo = 1;
// some other fields.
}
message Baz {
optional Bar bar = 1;
// some other fields.
}
もし既にBaz
メッセージを持っていて、Foo
内の深くネストされたval
を変更したい場合。代わりに
baz = baz.toBuilder().setBar(
baz.getBar().toBuilder().setFoo(
baz.getBar().getFoo().toBuilder().setVal(10).build()
).build()).build();
次のように記述できます
Baz.Builder builder = baz.toBuilder();
builder.getBarBuilder().getFooBuilder().setVal(10);
baz = builder.build();
ネストされた型
メッセージは別のメッセージの内部で宣言できます。例
message Foo {
message Bar { }
}
この場合、コンパイラは単純にBar
をFoo
の内部にネストされたインナークラスとして生成します。
フィールド
前述のメソッドに加えて、プロトコルバッファコンパイラは、.proto
ファイル内のメッセージに定義された各フィールドに対して、アクセッサメソッドのセットを生成します。フィールド値を読み取るメソッドは、メッセージクラスとその対応するビルダーの両方で定義されます。値を変更するメソッドは、ビルダーでのみ定義されます。
メソッド名は、.proto
ファイルのフィールド名がアンダースコアを含む小文字を使用している場合でも(そうあるべきですが)、常にキャメルケース命名を使用することに注意してください。ケース変換は次のように機能します
- 名前内の各アンダースコアについて、アンダースコアが削除され、続く文字が大文字になります。
- もし名前にプレフィックス(例: 「get」)が付加される場合、最初の文字は大文字になります。それ以外の場合は小文字になります。
- メソッド名内の各数字の最後の数字に続く文字は大文字になります。
したがって、フィールドfoo_bar_baz
はfooBarBaz
になります。get
がプレフィックスとして付くと、getFooBarBaz
になります。そしてfoo_ba23r_baz
はfooBa23RBaz
になります。
アクセッサメソッドと同様に、コンパイラは各フィールドに対してそのフィールド番号を含む整数定数を生成します。定数名は、フィールド名を大文字に変換し、その後ろに_FIELD_NUMBER
を付けたものです。例えば、フィールドoptional int32 foo_bar = 5;
が与えられた場合、コンパイラは定数public static final int FOO_BAR_FIELD_NUMBER = 5;
を生成します。
単一フィールド (proto2)
これらのフィールド定義のいずれかに対して
optional int32 foo = 1;
required int32 foo = 1;
コンパイラは、メッセージクラスとそのビルダーの両方に以下のアクセッサメソッドを生成します
boolean hasFoo()
: フィールドが設定されている場合、true
を返します。int getFoo()
: フィールドの現在の値を返します。フィールドが設定されていない場合、デフォルト値を返します。
コンパイラはメッセージのビルダーにのみ以下のメソッドを生成します
Builder setFoo(int value)
: フィールドの値を設定します。これを呼び出した後、hasFoo()
はtrue
を返し、getFoo()
はvalue
を返します。Builder clearFoo()
: フィールドの値をクリアします。これを呼び出した後、hasFoo()
はfalse
を返し、getFoo()
はデフォルト値を返します。
他の単純なフィールド型については、対応するJava型はスカラ値型テーブルに従って選択されます。メッセージ型と列挙型については、値型はメッセージまたは列挙クラスに置き換えられます。
埋め込みメッセージフィールド
メッセージ型の場合、setFoo()
はメッセージのビルダー型のインスタンスもパラメータとして受け入れます。これは、ビルダーで.build()
を呼び出し、その結果をメソッドに渡すのと同等のショートカットです。setFoo
に渡されたサブビルダーをさらに変更しても、メッセージクラスのビルダーには反映されません。メッセージクラスのビルダーは、サブメッセージの「所有権」を取ります。
もしフィールドが設定されていない場合、getFoo()
は、そのフィールドが何も設定されていないFooインスタンス(おそらくFoo.getDefaultInstance()
によって返されるインスタンス)を返します。
さらに、コンパイラはメッセージ型に関連するサブビルダーにアクセスできる2つのアクセッサメソッドを生成します。以下のメソッドは、メッセージクラスとそのビルダーの両方で生成されます
FooOrBuilder getFooOrBuilder()
: フィールドのビルダーが存在すればそれを返し、存在しなければメッセージを返します。ビルダーでこのメソッドを呼び出しても、フィールドのサブビルダーは作成されません。
コンパイラはメッセージのビルダーにのみ以下のメソッドを生成します。
Builder getFooBuilder()
: フィールドのビルダーを返します。
単一フィールド (proto3)
このフィールド定義の場合
int32 foo = 1;
コンパイラは、メッセージクラスとそのビルダーの両方に以下のアクセッサメソッドを生成します
int getFoo()
: フィールドの現在の値を返します。フィールドが設定されていない場合、そのフィールドの型のデフォルト値を返します。
コンパイラはメッセージのビルダーにのみ以下のメソッドを生成します
Builder setFoo(int value)
: フィールドの値を設定します。これを呼び出した後、getFoo()
はvalue
を返します。Builder clearFoo()
: フィールドの値をクリアします。これを呼び出した後、getFoo()
はそのフィールドの型のデフォルト値を返します。
他の単純なフィールド型については、対応するJava型はスカラ値型テーブルに従って選択されます。メッセージ型と列挙型については、値型はメッセージまたは列挙クラスに置き換えられます。
埋め込みメッセージフィールド
メッセージフィールド型の場合、メッセージクラスとそのビルダーの両方に追加のアクセッサメソッドが生成されます
boolean hasFoo()
: フィールドが設定されている場合、true
を返します。
setFoo()
はメッセージのビルダー型のインスタンスもパラメータとして受け入れます。これは、ビルダーで.build()
を呼び出し、その結果をメソッドに渡すのと同等のショートカットです。
もしフィールドが設定されていない場合、getFoo()
は、そのフィールドが何も設定されていないFooインスタンス(おそらくFoo.getDefaultInstance()
によって返されるインスタンス)を返します。
さらに、コンパイラはメッセージ型に関連するサブビルダーにアクセスできる2つのアクセッサメソッドを生成します。以下のメソッドは、メッセージクラスとそのビルダーの両方で生成されます
FooOrBuilder getFooOrBuilder()
: フィールドのビルダーが存在すればそれを返し、存在しなければメッセージを返します。ビルダーでこのメソッドを呼び出しても、フィールドのサブビルダーは作成されません。
コンパイラはメッセージのビルダーにのみ以下のメソッドを生成します。
Builder getFooBuilder()
: フィールドのビルダーを返します。
列挙型フィールド
列挙型フィールド型の場合、メッセージクラスとそのビルダーの両方に追加のアクセッサメソッドが生成されます
int getFooValue()
: 列挙型の整数値を返します。
コンパイラはメッセージのビルダーにのみ以下の追加メソッドを生成します
Builder setFooValue(int value)
: 列挙型の整数値を設定します。
さらに、列挙値が不明な場合、getFoo()
はUNRECOGNIZED
を返します。これはproto3コンパイラによって生成された列挙型に追加された特別な値です。
繰り返しフィールド
このフィールド定義の場合
repeated string foos = 1;
コンパイラは、メッセージクラスとそのビルダーの両方に以下のアクセッサメソッドを生成します
int getFoosCount()
: 現在フィールドにある要素の数を返します。String getFoos(int index)
: 指定されたゼロベースのインデックスにある要素を返します。ProtocolStringList getFoosList()
: フィールド全体をProtocolStringList
として返します。フィールドが設定されていない場合、空のリストを返します。
コンパイラはメッセージのビルダーにのみ以下のメソッドを生成します
Builder setFoos(int index, String value)
: 指定されたゼロベースのインデックスにある要素の値を設定します。Builder addFoos(String value)
: 指定された値を持つ新しい要素をフィールドに追加します。Builder addAllFoos(Iterable<? extends String> value)
: 指定されたIterable
内のすべての要素をフィールドに追加します。Builder clearFoos()
: フィールドからすべての要素を削除します。これを呼び出した後、getFoosCount()
はゼロを返します。
他の単純なフィールド型については、対応するJava型はスカラ値型テーブルに従って選択されます。メッセージ型と列挙型については、その型はメッセージまたは列挙クラスになります。
繰り返し埋め込みメッセージフィールド
メッセージ型の場合、setFoos()
およびaddFoos()
は、メッセージのビルダー型のインスタンスもパラメータとして受け入れます。これは、ビルダーで.build()
を呼び出し、その結果をメソッドに渡すのと同等のショートカットです。また、追加の生成メソッドがあります
Builder addFoos(int index, Field value)
: 指定されたゼロベースのインデックスに新しい要素を挿入します。現在のその位置にある要素(もしあれば)およびそれ以降の要素を右にシフトします(インデックスに1を加えます)。
さらに、コンパイラはメッセージ型に対して、メッセージクラスとそのビルダーの両方に以下の追加アクセッサメソッドを生成し、関連するサブビルダーにアクセスできるようにします
FooOrBuilder getFoosOrBuilder(int index)
: 指定された要素のビルダーが既に存在する場合はそれを返し、存在しない場合はIndexOutOfBoundsException
をスローします。メッセージクラスからこれを呼び出した場合、ビルダーではなく常にメッセージ(または例外)を返します。ビルダーでこのメソッドを呼び出しても、フィールドのサブビルダーは作成されません。List<FooOrBuilder> getFoosOrBuilderList()
: フィールド全体を、ビルダーの変更不可能なリスト(利用可能であれば)またはメッセージとして返します。メッセージクラスからこれを呼び出した場合、ビルダーの変更不可能なリストではなく、常にメッセージの不変リストを返します。
コンパイラはメッセージのビルダーにのみ以下のメソッドを生成します
Builder getFoosBuilder(int index)
: 指定されたインデックスにある要素のビルダーを返します。インデックスが範囲外の場合、IndexOutOfBoundsException
をスローします。Builder addFoosBuilder(int index)
: 指定されたインデックスに、繰り返しメッセージのデフォルトメッセージインスタンスのビルダーを挿入し、それを返します。既存のエントリは、挿入されたビルダーのためのスペースを確保するために、より高いインデックスにシフトされます。Builder addFoosBuilder()
: 繰り返しメッセージのデフォルトメッセージインスタンスのビルダーを追加し、それを返します。Builder removeFoos(int index)
: 指定されたゼロベースのインデックスにある要素を削除します。List<Builder> getFoosBuilderList()
: フィールド全体をビルダーの変更不可能なリストとして返します。
繰り返し列挙型フィールド (proto3のみ)
コンパイラは、メッセージクラスとそのビルダーの両方に以下の追加メソッドを生成します
int getFoosValue(int index)
: 指定されたインデックスにある列挙型の整数値を返します。List<java.lang.Integer> getFoosValueList()
: フィールド全体をIntegerのリストとして返します。
コンパイラはメッセージのビルダーにのみ以下の追加メソッドを生成します
Builder setFoosValue(int index, int value)
: 指定されたインデックスにある列挙型の整数値を設定します。
名前の衝突
もし別の非繰り返しフィールドが、繰り返しフィールドの生成メソッドのいずれかと名前が衝突する場合、両方のフィールド名の末尾にプロトコルバッファのフィールド番号が付加されます。
これらのフィールド定義の場合
int32 foos_count = 1;
repeated string foos = 2;
コンパイラはまずそれらを以下のようにリネームします
int32 foos_count_1 = 1;
repeated string foos_2 = 2;
アクセッサメソッドは、その後、上記で説明したように生成されます。
Oneof フィールド
このoneofフィールド定義の場合
oneof choice {
int32 foo_int = 4;
string foo_string = 9;
...
}
choice
oneof内のすべてのフィールドは、その値のために単一のプライベートフィールドを使用します。さらに、プロトコルバッファコンパイラは、oneofケースのJava列挙型を次のように生成します
public enum ChoiceCase
implements com.google.protobuf.Internal.EnumLite {
FOO_INT(4),
FOO_STRING(9),
...
CHOICE_NOT_SET(0);
...
};
この列挙型の値は以下の特別なメソッドを持ちます
int getNumber()
: .protoファイルで定義されたオブジェクトの数値的な値を返します。static ChoiceCase forNumber(int value)
: 指定された数値に対応する列挙型オブジェクトを返します(他の数値の場合はnull
)。
コンパイラは、メッセージクラスとそのビルダーの両方に以下のアクセッサメソッドを生成します
boolean hasFooInt()
: oneofケースがFOO_INT
の場合、true
を返します。int getFooInt()
: oneofケースがFOO_INT
の場合、foo
の現在の値を返します。それ以外の場合、このフィールドのデフォルト値を返します。ChoiceCase getChoiceCase()
: どのフィールドが設定されているかを示す列挙型を返します。いずれも設定されていない場合、CHOICE_NOT_SET
を返します。
コンパイラはメッセージのビルダーにのみ以下のメソッドを生成します
Builder setFooInt(int value)
:Foo
をこの値に設定し、oneofケースをFOO_INT
に設定します。これを呼び出した後、hasFooInt()
はtrue
を返し、getFooInt()
はvalue
を返し、getChoiceCase()
はFOO_INT
を返します。Builder clearFooInt()
:- oneofケースが
FOO_INT
でない場合、何も変更されません。 - oneofケースが
FOO_INT
の場合、Foo
をnullに設定し、oneofケースをCHOICE_NOT_SET
に設定します。これを呼び出した後、hasFooInt()
はfalse
を返し、getFooInt()
はデフォルト値を返し、getChoiceCase()
はCHOICE_NOT_SET
を返します。
- oneofケースが
Builder.clearChoice()
:choice
の値をリセットし、ビルダーを返します。
他の単純なフィールド型については、対応するJava型はスカラ値型テーブルに従って選択されます。メッセージ型と列挙型については、値型はメッセージまたは列挙クラスに置き換えられます。
マップフィールド
このマップフィールド定義の場合
map<int32, int32> weight = 1;
コンパイラは、メッセージクラスとそのビルダーの両方に以下のアクセッサメソッドを生成します
Map<Integer, Integer> getWeightMap();
: 変更不可能なMap
を返します。int getWeightOrDefault(int key, int default);
: キーの値が存在すればそれを返し、存在しない場合はデフォルト値を返します。int getWeightOrThrow(int key);
: キーの値が存在すればそれを返し、存在しない場合はIllegalArgumentExceptionをスローします。boolean containsWeight(int key);
: キーがこのフィールドに存在するかどうかを示します。int getWeightCount();
: マップ内の要素の数を返します。
コンパイラはメッセージのビルダーにのみ以下のメソッドを生成します
Builder putWeight(int key, int value);
: このフィールドにウェイトを追加します。Builder putAllWeight(Map<Integer, Integer> value);
: 指定されたマップ内のすべてのエントリをこのフィールドに追加します。Builder removeWeight(int key);
: このフィールドからウェイトを削除します。Builder clearWeight();
: このフィールドからすべてのウェイトを削除します。@Deprecated Map<Integer, Integer> getMutableWeight();
: 可変のMap
を返します。このメソッドを複数回呼び出すと、異なるマップインスタンスが返される可能性があることに注意してください。返されたマップ参照は、ビルダーへのその後のメソッド呼び出しによって無効になる場合があります。
メッセージ値マップフィールド
値としてメッセージ型を持つマップの場合、コンパイラはメッセージのビルダーに追加のメソッドを生成します
Foo.Builder putFooBuilderIfAbsent(int key);
: マッピングにkey
が存在することを確認し、まだ存在しない場合は新しいFoo.Builder
を挿入します。返されたFoo.Builder
への変更は、最終的なメッセージに反映されます。
Any
このようなAny
フィールドが与えられた場合
import "google/protobuf/any.proto";
message ErrorStatus {
string message = 1;
google.protobuf.Any details = 2;
}
生成されたコードでは、details
フィールドのゲッターはcom.google.protobuf.Any
のインスタンスを返します。これにより、Any
の値をパックおよびアンパックするための以下の特殊なメソッドが提供されます
class Any {
// Packs the given message into an Any using the default type URL
// prefix “type.googleapis.com”.
public static Any pack(Message message);
// Packs the given message into an Any using the given type URL
// prefix.
public static Any pack(Message message,
String typeUrlPrefix);
// Checks whether this Any message’s payload is the given type.
public <T extends Message> boolean is(class<T> clazz);
// Unpacks Any into the given message type. Throws exception if
// the type doesn’t match or parsing the payload has failed.
public <T extends Message> T unpack(class<T> clazz)
throws InvalidProtocolBufferException;
}
列挙型
以下のような列挙型定義が与えられた場合
enum Foo {
VALUE_A = 0;
VALUE_B = 5;
VALUE_C = 1234;
}
プロトコルバッファコンパイラは、同じ値のセットを持つFoo
というJava列挙型を生成します。proto3を使用している場合、列挙型に特殊な値UNRECOGNIZED
も追加されます。生成された列挙型の値は以下の特別なメソッドを持ちます
int getNumber()
:.proto
ファイルで定義されたオブジェクトの数値的な値を返します。EnumValueDescriptor getValueDescriptor()
: 値の記述子を返します。これには、値の名前、番号、型に関する情報が含まれています。EnumDescriptor getDescriptorForType()
: 列挙型の記述子を返します。これには、定義された各値に関する情報などが含まれています。
さらに、Foo
列挙型は以下の静的メソッドを含みます
static Foo forNumber(int value)
: 指定された数値に対応する列挙型オブジェクトを返します。対応する列挙型オブジェクトがない場合はnullを返します。static Foo valueOf(int value)
: 指定された数値に対応する列挙型オブジェクトを返します。このメソッドはforNumber(int value)
に代わって非推奨となり、今後のリリースで削除される予定です。static Foo valueOf(EnumValueDescriptor descriptor)
: 指定された値記述子に対応する列挙型オブジェクトを返します。valueOf(int)
よりも高速である場合があります。proto3では、未知の値記述子が渡された場合、UNRECOGNIZED
を返します。EnumDescriptor getDescriptor()
: 列挙型の記述子を返します。これには、定義された各値に関する情報などが含まれています。(これは、静的メソッドであるという点でのみgetDescriptorForType()
と異なります。)
各列挙値に対して、_VALUEサフィックスを持つ整数定数も生成されます。
.proto
言語では、複数の列挙シンボルが同じ数値を持つことができることに注意してください。同じ数値を持つシンボルは同義語です。例
enum Foo {
BAR = 0;
BAZ = 0;
}
この場合、BAZ
はBAR
の同義語です。Javaでは、BAZ
は次のように静的なfinalフィールドとして定義されます
static final Foo BAZ = BAR;
したがって、BAR
とBAZ
は等しいと比較され、BAZ
はswitch文に現れるべきではありません。コンパイラは、与えられた数値で定義された最初のシンボルをそのシンボルの「正規」バージョンとして常に選択します。同じ数値を持つすべての後続のシンボルは単なるエイリアスです。
列挙型はメッセージ型内にネストして定義できます。コンパイラは、そのメッセージ型のクラス内にネストされたJava列挙型定義を生成します。
注意: Javaコードを生成する場合、protobufの列挙型の値の最大数は驚くほど少なくなる可能性があります—最悪の場合、最大値は1,700値をわずかに超える程度です。この制限は、Javaバイトコードのメソッドごとのサイズ制限に起因し、Javaの実装、protobufスイートの異なるバージョン、および.proto
ファイルで列挙型に設定されたオプションによって異なります。
拡張 (proto2のみ)
拡張範囲を持つメッセージが与えられた場合
message Foo {
extensions 100 to 199;
}
プロトコルバッファコンパイラは、通常のGeneratedMessage
の代わりにFoo
にGeneratedMessage.ExtendableMessage
を継承させます。同様に、Foo
のビルダーはGeneratedMessage.ExtendableBuilder
を継承します。これらの基本型を名前で参照してはなりません(GeneratedMessage
は実装の詳細とみなされます)。しかし、これらのスーパークラスは、拡張を操作するために使用できる多数の追加メソッドを定義します。
特に、Foo
とFoo.Builder
は、hasExtension()
、getExtension()
、getExtensionCount()
のメソッドを継承します。さらに、Foo.Builder
はsetExtension()
とclearExtension()
のメソッドを継承します。これらの各メソッドは、最初のパラメータとして拡張識別子(後述)を取り、これは拡張フィールドを識別します。残りのパラメータと戻り値は、拡張識別子と同じ型の通常の(拡張ではない)フィールドに対して生成される対応するアクセッサメソッドのものとまったく同じです。
拡張定義が与えられた場合
extend Foo {
optional int32 bar = 123;
}
プロトコルバッファコンパイラは、bar
という「拡張識別子」を生成します。これは、Foo
の拡張アクセッサとともに使用して、この拡張にアクセスできます。以下のように。
Foo foo =
Foo.newBuilder()
.setExtension(bar, 1)
.build();
assert foo.hasExtension(bar);
assert foo.getExtension(bar) == 1;
(拡張識別子の正確な実装は複雑で、ジェネリクスを魔法のように使用しますが、それらを使用するために拡張識別子がどのように機能するかを心配する必要はありません。)
上記で説明したように、bar
は.proto
ファイルのラッパークラスの静的フィールドとして宣言されることに注意してください。例ではラッパークラス名を省略しています。
拡張は、生成されるシンボル名にプレフィックスを付けるために、別の型のスコープ内で宣言できます。例えば、一般的なパターンは、フィールドの型の宣言の内部で、フィールドによってメッセージを拡張することです
message Baz {
extend Foo {
optional Baz foo_ext = 124;
}
}
この場合、識別子foo_ext
と型Baz
を持つ拡張は、Baz
の宣言の内部で宣言され、foo_ext
を参照するにはBaz.
プレフィックスの追加が必要です
Baz baz = createMyBaz();
Foo foo =
Foo.newBuilder()
.setExtension(Baz.fooExt, baz)
.build();
assert foo.hasExtension(Baz.fooExt);
assert foo.getExtension(Baz.fooExt) == baz;
拡張を持つ可能性のあるメッセージをパースする場合、パースしたい拡張を登録したExtensionRegistry
を提供する必要があります。そうしないと、それらの拡張は不明なフィールドとして扱われ、拡張を監視するメソッドは存在しないかのように振る舞います。
ExtensionRegistry registry = ExtensionRegistry.newInstance();
registry.add(Baz.fooExt);
Foo foo = Foo.parseFrom(input, registry);
assert foo.hasExtension(Baz.fooExt);
ExtensionRegistry registry = ExtensionRegistry.newInstance();
Foo foo = Foo.parseFrom(input, registry);
assert foo.hasExtension(Baz.fooExt) == false;
サービス
もし.proto
ファイルが以下の行を含んでいる場合
option java_generic_services = true;
その場合、プロトコルバッファコンパイラは、このセクションで説明されているように、ファイル内で見つかったサービス定義に基づいてコードを生成します。しかし、生成されたコードは特定のRPCシステムに結び付けられていないため、1つのシステムに特化したコードよりも多くの間接参照レベルが必要となり、望ましくない場合があります。このコードが生成されるのを望まない場合は、以下の行をファイルに追加してください
option java_generic_services = false;
上記いずれの行も指定されていない場合、汎用サービスが非推奨であるため、オプションはデフォルトでfalse
になります。(2.4.0より前は、オプションのデフォルトはtrue
でした。)
.proto
言語のサービス定義に基づくRPCシステムは、そのシステムに適したコードを生成するためのプラグインを提供する必要があります。これらのプラグインは、同じ名前の独自のクラスを生成できるように、抽象サービスが無効になっていることを要求する可能性が高いです。プラグインはバージョン2.3.0(2010年1月)で新しく導入されました。
本セクションの残りの部分では、抽象サービスが有効になっている場合にプロトコルバッファコンパイラが何を生成するかについて説明します。
インターフェース
サービス定義が与えられた場合
service Foo {
rpc Bar(FooRequest) returns(FooResponse);
}
プロトコルバッファコンパイラは、このサービスを表す抽象クラスFoo
を生成します。Foo
は、サービス定義で定義された各メソッドに対して抽象メソッドを持ちます。この場合、メソッドBar
は次のように定義されます
abstract void bar(RpcController controller, FooRequest request,
RpcCallback<FooResponse> done);
パラメータはService.CallMethod()
のパラメータと同等ですが、method
引数が暗黙的であり、request
とdone
がその正確な型を指定する点が異なります。
Foo
はService
インターフェースのサブクラスです。プロトコルバッファコンパイラは、Service
のメソッドの実装を次のように自動的に生成します
getDescriptorForType
: サービスのServiceDescriptor
を返します。callMethod
: 提供されたメソッド記述子に基づいてどのメソッドが呼び出されているかを判断し、リクエストメッセージとコールバックを正しい型にダウンキャストして直接呼び出します。getRequestPrototype
およびgetResponsePrototype
: 指定されたメソッドの正しい型のリクエストまたはレスポンスのデフォルトインスタンスを返します。
以下の静的メソッドも生成されます
static ServiceDescriptor getServiceDescriptor()
: 型の記述子を返します。これには、このサービスがどのようなメソッドを持ち、それらの入力および出力型が何であるかに関する情報が含まれています。
Foo
はネストされたインターフェースFoo.Interface
も含まれます。これは、サービス定義内の各メソッドに対応するメソッドを再度含む純粋なインターフェースです。ただし、このインターフェースはService
インターフェースを拡張しません。これは、RPCサーバーの実装が通常、特定のサービスではなく抽象的なService
オブジェクトを使用するように書かれているため問題です。この問題を解決するには、Foo.Interface
を実装するオブジェクトimpl
がある場合、Foo.newReflectiveService(impl)
を呼び出して、単にimpl
に委譲し、Service
を実装するFoo
のインスタンスを構築できます。
要約すると、独自のサービスを実装する際には2つのオプションがあります
Foo
をサブクラス化し、そのメソッドを適切に実装してから、サブクラスのインスタンスをRPCサーバーの実装に直接渡します。これは通常最も簡単ですが、一部の人々はこれを「純粋でない」と見なします。Foo.Interface
を実装し、Foo.newReflectiveService(Foo.Interface)
を使用してそれをラップするService
を構築し、その後そのラッパーをRPC実装に渡します。
スタブ
プロトコルバッファコンパイラは、すべてのサービスインターフェースの「スタブ」実装も生成します。これは、サービスを実装するサーバーにリクエストを送信したいクライアントによって使用されます。上記のFoo
サービスの場合、スタブ実装Foo.Stub
はネストされたクラスとして定義されます。
Foo.Stub
はFoo
のサブクラスであり、以下のメソッドも実装します
Foo.Stub(RpcChannel channel)
: 指定されたチャネルでリクエストを送信する新しいスタブを構築します。RpcChannel getChannel()
: このスタブのチャネルを、コンストラクタに渡されたものとして返します。
スタブはさらに、サービスの各メソッドをチャネルのラッパーとして実装します。いずれかのメソッドを呼び出すと、単にchannel.callMethod()
が呼び出されます。
Protocol BuffersライブラリはRPC実装を含みません。しかし、生成されたサービスクラスを選択した任意のRPC実装に接続するために必要なすべてのツールが含まれています。RpcChannel
とRpcController
の実装を提供するだけで済みます。
ブロッキングインターフェース
上記で説明したRPCクラスはすべて非ブロッキングセマンティクスを持っています。つまり、メソッドを呼び出すと、メソッドが完了したときに呼び出されるコールバックオブジェクトを提供します。多くの場合、ブロッキングセマンティクスを使用してコードを書く方が簡単です(ただし、スケーラビリティは低いかもしれません)。そこでは、メソッドは完了するまで戻りません。これに対応するため、プロトコルバッファコンパイラはサービスクラスのブロッキングバージョンも生成します。Foo.BlockingInterface
はFoo.Interface
と同等ですが、各メソッドがコールバックを呼び出すのではなく、単に結果を返す点が異なります。したがって、例えば、bar
は次のように定義されます
abstract FooResponse bar(RpcController controller, FooRequest request)
throws ServiceException;
非ブロッキングサービスと同様に、Foo.newReflectiveBlockingService(Foo.BlockingInterface)
は、何らかのFoo.BlockingInterface
をラップするBlockingService
を返します。最後に、Foo.BlockingStub
は、特定のBlockingRpcChannel
にリクエストを送信するFoo.BlockingInterface
のスタブ実装を返します。
プラグイン挿入ポイント
コードジェネレータプラグインがJavaコードジェネレータの出力を拡張したい場合、指定された挿入ポイント名を使用して以下の種類のコードを挿入できます。
outer_class_scope
: ファイルのラッパークラスに属するメンバー宣言。class_scope:TYPENAME
: メッセージクラスに属するメンバー宣言。TYPENAME
は完全なproto名、例えばpackage.MessageType
です。builder_scope:TYPENAME
: メッセージのビルダークラスに属するメンバー宣言。TYPENAME
は完全なproto名、例えばpackage.MessageType
です。enum_scope:TYPENAME
: 列挙型クラスに属するメンバー宣言。TYPENAME
は完全なproto列挙型名、例えばpackage.EnumType
です。message_implements:TYPENAME
: メッセージクラスのクラス実装宣言。TYPENAME
は完全なproto名、例えばpackage.MessageType
です。builder_implements:TYPENAME
: ビルダークラスのクラス実装宣言。TYPENAME
は完全なproto名、例えばpackage.MessageType
です。
生成されたコードはインポート文を含むことができません。これらは生成されたコード自体の中で定義された型名と衝突する可能性があるためです。代わりに、外部クラスを参照する際には、常にその完全修飾名を使用する必要があります。
Javaコードジェネレータにおける出力ファイル名を決定するロジックはかなり複雑です。すべてのケースをカバーしていることを確認するには、protoc
のソースコード、特にjava_headers.cc
を参照することをお勧めします。
標準コードジェネレータによって宣言されたプライベートクラスメンバーに依存するコードを生成しないでください。これらの実装の詳細は、Protocol Buffersの将来のバージョンで変更される可能性があります。
ユーティリティクラス
Protocol Buffersは、メッセージ比較、JSON変換、および既知の型(一般的なユースケース向けの定義済みプロトコルバッファメッセージ)の操作のためのユーティリティクラスを提供します。