Java生成コードガイド

プロトコルバッファコンパイラが任意のプロトコル定義に対してどのような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.protoFooBarというクラス名を生成します。もしファイル内に同じ名前のサービス、列挙型、またはメッセージ(ネストされた型を含む)がある場合、ラッパークラスの名前に「OuterClass」が追加されます。例

  • もしfoo_bar.protoFooBarというメッセージが含まれている場合、ラッパークラスはFooBarOuterClassというクラス名を生成します。
  • もしfoo_bar.protoFooServiceというサービスが含まれており、かつjava_outer_classnameも文字列FooServiceに設定されている場合、ラッパークラスはFooServiceOuterClassというクラス名を生成します。

ネストされたクラスに加えて、ラッパークラス自体は以下の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/genbuildは作成されません。これらは既に存在している必要があります。単一の呼び出しで複数の.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として宣言されており、さらなるサブクラス化は許可されません。FooGeneratedMessageを継承しますが、これは実装の詳細とみなされるべきです。デフォルトでは、FooGeneratedMessageの多くのメソッドを、最大速度のための特殊なバージョンでオーバーライドします。ただし、もし.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.BuilderMessage.Builderインターフェースを実装します。これはGeneratedMessage.Builderクラスを継承しますが、これもまた実装の詳細とみなされるべきです。Fooと同様に、Foo.BuilderGeneratedMessage.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 { }
}

この場合、コンパイラは単純にBarFooの内部にネストされたインナークラスとして生成します。

フィールド

前述のメソッドに加えて、プロトコルバッファコンパイラは、.protoファイル内のメッセージに定義された各フィールドに対して、アクセッサメソッドのセットを生成します。フィールド値を読み取るメソッドは、メッセージクラスとその対応するビルダーの両方で定義されます。値を変更するメソッドは、ビルダーでのみ定義されます。

メソッド名は、.protoファイルのフィールド名がアンダースコアを含む小文字を使用している場合でも(そうあるべきですが)、常にキャメルケース命名を使用することに注意してください。ケース変換は次のように機能します

  • 名前内の各アンダースコアについて、アンダースコアが削除され、続く文字が大文字になります。
  • もし名前にプレフィックス(例: 「get」)が付加される場合、最初の文字は大文字になります。それ以外の場合は小文字になります。
  • メソッド名内の各数字の最後の数字に続く文字は大文字になります。

したがって、フィールドfoo_bar_bazfooBarBazになります。getがプレフィックスとして付くと、getFooBarBazになります。そしてfoo_ba23r_bazfooBa23RBazになります。

アクセッサメソッドと同様に、コンパイラは各フィールドに対してそのフィールド番号を含む整数定数を生成します。定数名は、フィールド名を大文字に変換し、その後ろに_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を返します。
  • 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;
}

この場合、BAZBARの同義語です。Javaでは、BAZは次のように静的なfinalフィールドとして定義されます

static final Foo BAZ = BAR;

したがって、BARBAZは等しいと比較され、BAZはswitch文に現れるべきではありません。コンパイラは、与えられた数値で定義された最初のシンボルをそのシンボルの「正規」バージョンとして常に選択します。同じ数値を持つすべての後続のシンボルは単なるエイリアスです。

列挙型はメッセージ型内にネストして定義できます。コンパイラは、そのメッセージ型のクラス内にネストされたJava列挙型定義を生成します。

注意: Javaコードを生成する場合、protobufの列挙型の値の最大数は驚くほど少なくなる可能性があります—最悪の場合、最大値は1,700値をわずかに超える程度です。この制限は、Javaバイトコードのメソッドごとのサイズ制限に起因し、Javaの実装、protobufスイートの異なるバージョン、および.protoファイルで列挙型に設定されたオプションによって異なります。

拡張 (proto2のみ)

拡張範囲を持つメッセージが与えられた場合

message Foo {
  extensions 100 to 199;
}

プロトコルバッファコンパイラは、通常のGeneratedMessageの代わりにFooGeneratedMessage.ExtendableMessageを継承させます。同様に、FooのビルダーはGeneratedMessage.ExtendableBuilderを継承します。これらの基本型を名前で参照してはなりません(GeneratedMessageは実装の詳細とみなされます)。しかし、これらのスーパークラスは、拡張を操作するために使用できる多数の追加メソッドを定義します。

特に、FooFoo.Builderは、hasExtension()getExtension()getExtensionCount()のメソッドを継承します。さらに、Foo.BuildersetExtension()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引数が暗黙的であり、requestdoneがその正確な型を指定する点が異なります。

FooServiceインターフェースのサブクラスです。プロトコルバッファコンパイラは、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.StubFooのサブクラスであり、以下のメソッドも実装します

  • Foo.Stub(RpcChannel channel): 指定されたチャネルでリクエストを送信する新しいスタブを構築します。
  • RpcChannel getChannel(): このスタブのチャネルを、コンストラクタに渡されたものとして返します。

スタブはさらに、サービスの各メソッドをチャネルのラッパーとして実装します。いずれかのメソッドを呼び出すと、単にchannel.callMethod()が呼び出されます。

Protocol BuffersライブラリはRPC実装を含みません。しかし、生成されたサービス​​クラスを選択した任意のRPC実装に接続するために必要なすべてのツールが含まれています。RpcChannelRpcControllerの実装を提供するだけで済みます。

ブロッキングインターフェース

上記で説明したRPCクラスはすべて非ブロッキングセマンティクスを持っています。つまり、メソッドを呼び出すと、メソッドが完了したときに呼び出されるコールバックオブジェクトを提供します。多くの場合、ブロッキングセマンティクスを使用してコードを書く方が簡単です(ただし、スケーラビリティは低いかもしれません)。そこでは、メソッドは完了するまで戻りません。これに対応するため、プロトコルバッファコンパイラはサービス​​クラスのブロッキングバージョンも生成します。Foo.BlockingInterfaceFoo.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変換、および既知の型(一般的なユースケース向けの定義済みプロトコルバッファメッセージ)の操作のためのユーティリティクラスを提供します。