Java 生成コードガイド

指定されたプロトコル定義に対して、プロトコルバッファコンパイラが exactly どのような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` というクラス名を生成します。

ネストされたクラスに加えて、ラッパークラス自体は次の 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 コードを出力する場合、多くの Java ツールが JAR ファイルから直接ソースコードを読み取ることができるため、プロトコルバッファコンパイラの 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 プロト名に関する推奨事項を参照してください。

単純なメッセージ宣言を考えます。

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` にのみリンクする必要があります。「ライト」ライブラリはフルライブラリよりもはるかに小さく、携帯電話などのリソースが制約されたシステムに適しています。

`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.Builder` は静的メソッド `Foo.newBuilder()` を呼び出すことによって取得できます。

`Foo.Builder` は静的メソッドを定義しません。そのインターフェースは `Message.Builder` インターフェースによって定義されたものとまったく同じですが、戻り型がより具体的であるという例外があります。ビルダーを変更する `Foo.Builder` のメソッドは `Foo.Builder` 型を返し、`build()` は `Foo` 型を返します。

フィールドセッターを含むビルダーの内容を変更するメソッドは、常にビルダーへの参照を返します(つまり、「`return this;`」を返します)。これにより、複数のメソッド呼び出しを1行で連結できます。例: `builder.mergeFrom(obj).setFoo(1).setBar("abc").clearBaz();`

ビルダーはスレッドセーフではないため、複数の異なるスレッドが単一のビルダーの内容を変更する必要がある場合は、Java の同期を使用する必要があることに注意してください。

サブビルダー

サブメッセージを含むメッセージの場合、コンパイラはサブビルダーも生成します。これにより、深くネストされたサブメッセージを再構築することなく繰り返し変更できます。例:

message Foo {
  int32 val = 1;
  // some other fields.
}

message Bar {
  Foo foo = 1;
  // some other fields.
}

message Baz {
  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` を付けたものです。たとえば、`int32 foo_bar = 5;` というフィールドが与えられた場合、コンパイラは定数 `public static final int FOO_BAR_FIELD_NUMBER = 5;` を生成します。

以下のセクションは、明示的な存在と暗黙的な存在に分かれています。Proto2 は明示的な存在を持ち、proto3 はデフォルトで暗黙的な存在を持ちます。エディションはデフォルトで明示的な存在ですが、`features.field_presence` を使用してオーバーライドできます。

明示的なプレゼンスを持つ単数フィールド

これらのフィールド定義のいずれかに対して

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()`: フィールドのビルダーを返します。

暗黙的なプレゼンスを持つ単数フィールド

このフィールド定義について:

int32 foo = 1;

コンパイラは、メッセージクラスとそのビルダーの両方に次のアクセサメソッドを生成します。

  • `int getFoo()`: フィールドの現在の値を返します。フィールドが設定されていない場合、フィールドの型のデフォルト値を返します。

コンパイラは、メッセージのビルダーのみに次のメソッドを生成します。

  • `Builder setFoo(int value)`: フィールドの値を設定します。これを呼び出した後、`getFoo()` は `value` を返します。
  • `Builder clearFoo()`: フィールドの値をクリアします。これを呼び出した後、`getFoo()` はフィールドの型のデフォルト値を返します。

他の単純なフィールド型については、対応する Java 型はスカラー値型テーブルに従って選択されます。メッセージ型と列挙型については、値型はメッセージまたは列挙クラスに置き換えられます。

列挙型フィールド

列挙型フィールドの場合、メッセージクラスとそのビルダーの両方で追加のアクセサメソッドが生成されます。

  • `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()`: フィールド全体を整数のリストとして返します。

コンパイラは、メッセージのビルダーのみに次の追加メソッドを生成します。

  • `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` を返します。
  • ビルダー 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フィールド定義の場合:

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);

  // Checks whether this Any message’s payload has the same type as the given
  // message.
  public boolean isSameTypeAs(Message message);

  // Unpacks Any into a message with the same type as the given messsage.
  // Throws exception if the type doesn’t match or parsing the payload fails.
  public <T extends Message> T unpackSameTypeAs(T message)
      throws InvalidProtocolBufferException;

  // 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定義が与えられた場合

enum Foo {
  VALUE_A = 0;
  VALUE_B = 5;
  VALUE_C = 1234;
}

プロトコルバッファコンパイラは、同じ値セットを持つ `Foo` という Java 列挙型を生成します。proto3 を使用している場合、列挙型に特殊な値 `UNRECOGNIZED` も追加されます。エディションでは、`OPEN` 列挙型にも `UNRECOGNIZED` 値がありますが、`CLOSED` 列挙型にはありません。生成された列挙型の値には、次の特殊なメソッドがあります。

  • `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 および `OPEN` 列挙型では、不明な値記述子が渡された場合、`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` ファイルで列挙型に設定されたオプションによって異なります。

エクステンション

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

edition = "2023";

message Foo {
  extensions 100 to 199;
}

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

特に、`Foo` と `Foo.Builder` は `hasExtension()`、`getExtension()`、`getExtensionCount()` メソッドを継承します。さらに、`Foo.Builder` は `setExtension()` と `clearExtension()` メソッドを継承します。これらの各メソッドは、最初のパラメータとして拡張識別子(下記で説明)を受け取り、これにより拡張フィールドが識別されます。残りのパラメータと戻り値は、拡張識別子と同じ型の通常の(非拡張)フィールドに対して生成される対応するアクセサメソッドのものとまったく同じです。

エクステンション定義が与えられた場合:

edition = "2023";

import "foo.proto";

extend Foo {
  int32 bar = 123;
}

プロトコルバッファコンパイラは `bar` という「拡張識別子」を生成します。これは、`Foo` の拡張アクセサと共に使用して、次のようにこの拡張にアクセスできます。

Foo foo =
  Foo.newBuilder()
     .setExtension(bar, 1)
     .build();
assert foo.hasExtension(bar);
assert foo.getExtension(bar) == 1;

(拡張識別子の正確な実装は複雑で、ジェネリクスを魔法のように使用しますが、拡張識別子がどのように機能するかを理解する必要はありません。)

`bar` は、上記のように、`.proto` ファイルのラッパークラスの静的フィールドとして宣言されることに注意してください。例ではラッパークラス名を省略しています。

拡張機能は、生成されるシンボル名にプレフィックスを付けるために、別の型のスコープ内で宣言できます。たとえば、一般的なパターンは、フィールドの型の宣言*内*でメッセージをフィールドによって拡張することです。

edition = "2023";

import "foo.proto";

message Baz {
  extend Foo {
    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 システムは、システムに適したコードを生成するためのプラグインを提供する必要があります。これらのプラグインは、同じ名前の独自のクラスを生成できるように、抽象サービスを無効にすることを要求する可能性が高いです。

このセクションの残りの部分では、抽象サービスが有効になっている場合に Protocol Buffer コンパイラが何を生成するかについて説明します。

インターフェース

サービス定義が与えられた場合

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` はその正確な型を指定します。

FooServiceインターフェースをサブクラス化します。Protocol Bufferコンパイラは、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 Buffer ライブラリには 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` は完全なプロト名です(例: `package.MessageType`)。
  • `builder_scope:TYPENAME`: メッセージのビルダークラスに属するメンバ宣言。`TYPENAME` は完全なプロト名です(例: `package.MessageType`)。
  • `enum_scope:TYPENAME`: 列挙型クラスに属するメンバ宣言。`TYPENAME` は完全なプロト列挙名です(例: `package.EnumType`)。
  • `message_implements:TYPENAME`: メッセージクラスのクラス実装宣言。`TYPENAME` は完全なプロト名です(例: `package.MessageType`)。
  • `builder_implements:TYPENAME`: ビルダークラスのクラス実装宣言。`TYPENAME` は完全なプロト名です(例: `package.MessageType`)。

生成されたコードにはインポートステートメントを含めることはできません。これらは生成されたコード自体で定義されている型名と衝突する可能性が高いためです。代わりに、外部クラスを参照する場合は、常にその完全修飾名を使用する必要があります。

Java コードジェネレーターにおける出力ファイル名を決定するロジックはかなり複雑です。すべてのケースを網羅していることを確認するには、おそらく `protoc` のソースコード、特に `java_headers.cc` を参照する必要があります。

標準のコードジェネレータによって宣言されたプライベートクラスメンバーに依存するコードは生成しないでください。これらの実装詳細は、Protocol Buffers の将来のバージョンで変更される可能性があります。

ユーティリティクラス

プロトコルバッファは、メッセージ比較、JSON 変換、およびよく知られている型(一般的なユースケース向けの事前定義されたプロトコルバッファメッセージ)を操作するためのユーティリティクラスを提供します。