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
パッケージに配置されます。通常の .proto
package
宣言は、逆ドメイン名で始まることは想定されていないため、java_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()
: 型の記述子を返します。これには、型が持つフィールドとその型など、型に関する情報が含まれています。これは、getField()
などのMessage
のリフレクションメソッドで使用できます。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 {
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()
を呼び出し、結果をメソッドに渡すのと同じショートカットです。
フィールドが設定されていない場合、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()
: フィールドのビルダーを返します。
Enum フィールド
列挙フィールド型の場合、追加のアクセサメソッドがメッセージクラスとビルダーの両方で生成されます。
int getFooValue()
: 列挙型の整数値を返します。
コンパイラは、メッセージのビルダーでのみ次の追加メソッドを生成します。
Builder setFooValue(int value)
: 列挙型の整数値を設定します。
さらに、列挙値が不明な場合、getFoo()
は UNRECOGNIZED
を返します。これは、proto3 コンパイラによって生成された 列挙型に追加された特別な追加の値です。
繰り返しフィールド
このフィールド定義の場合
repeated string foos = 1;
コンパイラは、メッセージクラスとビルダーの両方で次のアクセサメソッドを生成します。
int getFoosCount()
: フィールドに現在存在する要素の数を返します。String getFoos(int index)
: 指定された 0 ベースのインデックスにある要素を返します。ProtocolStringList getFoosList()
: フィールド全体をProtocolStringList
として返します。フィールドが設定されていない場合は、空のリストを返します。
コンパイラは、メッセージのビルダーでのみ次のメソッドを生成します。
Builder setFoos(int index, String value)
: 指定された 0 ベースのインデックスにある要素の値を設定します。Builder addFoos(String value)
: 指定された値を持つ新しい要素をフィールドに追加します。Builder addAllFoos(Iterable<? extends String> value)
: 指定されたIterable
内のすべての要素をフィールドに追加します。Builder clearFoos()
: フィールドからすべての要素を削除します。これを呼び出すと、getFoosCount()
はゼロを返します。
他の単純なフィールド型の場合、対応する Java 型は、スカラー値型テーブルに従って選択されます。メッセージ型と列挙型の場合、型はメッセージまたは列挙クラスです。
繰り返し埋め込みメッセージフィールド
メッセージ型の場合、setFoos()
と addFoos()
は、パラメータとしてメッセージのビルダー型のインスタンスも受け入れます。これは、ビルダーで .build()
を呼び出し、結果をメソッドに渡すのと同じショートカットです。追加の生成されたメソッドもあります
Builder addFoos(int index, Field value)
: 指定された 0 ベースのインデックスに新しい要素を挿入します。その位置 (存在する場合) と後続の要素を右にシフトします (インデックスに 1 を加算します)。
さらに、コンパイラは、メッセージ型について、メッセージクラスとビルダーの両方で次の追加のアクセサメソッドを生成し、関連するサブビルダーにアクセスできるようにします。
FooOrBuilder getFoosOrBuilder(int index)
: 指定された要素のビルダーが既に存在する場合はビルダーを返し、存在しない場合はIndexOutOfBoundsException
をスローします。これがメッセージクラスから呼び出された場合、常にビルダーではなくメッセージ (または例外をスロー) を返します。ビルダーでこのメソッドを呼び出しても、フィールドのサブビルダーは作成されません。List<FooOrBuilder> getFoosOrBuilderList()
: フィールド全体をビルダーの変更不可能なリスト (使用可能な場合) またはメッセージ (使用できない場合) として返します。これがメッセージクラスから呼び出された場合、常にビルダーの変更不可能なリストではなく、メッセージの不変リストを返します。
コンパイラは、メッセージのビルダーでのみ次のメソッドを生成します。
Builder getFoosBuilder(int index)
: 指定されたインデックスにある要素のビルダーを返すか、インデックスが範囲外の場合はIndexOutOfBoundsException
をスローします。Builder addFoosBuilder(int index)
: 指定されたインデックスにある繰り返しメッセージのデフォルトメッセージインスタンスのビルダーを挿入して返します。既存のエントリは、挿入されたビルダーのためにスペースを作るために、より高いインデックスにシフトされます。Builder addFoosBuilder()
: 繰り返しメッセージのデフォルトメッセージインスタンスのビルダーを追加して返します。Builder removeFoos(int index)
: 指定された 0 ベースのインデックスにある要素を削除します。List<Builder> getFoosBuilderList()
: フィールド全体をビルダーの変更不可能なリストとして返します。
繰り返し Enum フィールド (proto3 のみ)
コンパイラは、メッセージクラスとビルダーの両方で次の追加メソッドを生成します。
int getFoosValue(int index)
: 指定されたインデックスにある列挙型の整数値を返します。List<java.lang.Integer> getFoosValueList()
: フィールド全体を整数のリストとして返します。
コンパイラは、メッセージのビルダーでのみ次の追加メソッドを生成します。
Builder setFoosValue(int index, int value)
: 指定されたインデックスにある列挙型の整数値を設定します。
名前の衝突
別の非繰り返しフィールドの名前が、繰り返しフィールドの生成されたメソッドの 1 つと競合する場合、両方のフィールド名に protobuf フィールド番号が末尾に追加されます。
これらのフィールド定義の場合
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 フィールド
この 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);
: このフィールドに weight を追加します。Builder putAllWeight(Map<Integer, Integer> value);
: 指定されたマップ内のすべてのエントリをこのフィールドに追加します。Builder removeWeight(int key);
: このフィールドから weight を削除します。Builder clearWeight();
: このフィールドからすべての weight を削除します。@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
フィールドの getter は 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 定義が与えられた場合
enum Foo {
VALUE_A = 0;
VALUE_B = 5;
VALUE_C = 1234;
}
プロトコルバッファコンパイラは、同じ値のセットを持つ Foo
という名前の Java enum 型を生成します。proto3 を使用している場合、enum 型に特別な値 UNRECOGNIZED
も追加します。生成された enum 型の値には、次の特殊メソッドがあります。
int getNumber()
:.proto
ファイルで定義されているオブジェクトの数値を返します。EnumValueDescriptor getValueDescriptor()
: 値の記述子を返します。これには、値の名前、数値、および型に関する情報が含まれています。EnumDescriptor getDescriptorForType()
: enum 型の記述子を返します。これには、定義された各値に関する情報などが含まれています。
さらに、Foo
enum 型には、次の静的メソッドが含まれています。
static Foo forNumber(int value)
: 指定された数値に対応する enum オブジェクトを返します。対応する enum オブジェクトがない場合は null を返します。static Foo valueOf(int value)
: 指定された数値に対応する enum オブジェクトを返します。このメソッドはforNumber(int value)
を優先して非推奨とされており、今後のリリースで削除される予定です。static Foo valueOf(EnumValueDescriptor descriptor)
: 指定された値記述子に対応する enum オブジェクトを返します。valueOf(int)
よりも高速な場合があります。proto3 では、不明な値記述子が渡されるとUNRECOGNIZED
を返します。EnumDescriptor getDescriptor()
: enum 型の記述子を返します。これには、定義された各値に関する情報などが含まれています。(これは、静的メソッドであるという点を除いて、getDescriptorForType()
とのみ異なります。)
整数定数も、各 enum 値のサフィックス _VALUE で生成されます。
.proto
言語では、複数の enum シンボルが同じ数値を持つことができることに注意してください。同じ数値を持つシンボルは同義語です。例
enum Foo {
BAR = 0;
BAZ = 0;
}
この場合、BAZ
は BAR
の同義語です。Java では、BAZ
は次のような静的 final フィールドとして定義されます。
static final Foo BAZ = BAR;
したがって、BAR
と BAZ
は等しく比較され、BAZ
は switch ステートメントに決して表示されるべきではありません。コンパイラは常に、特定の数値で定義された最初のシンボルをそのシンボルの「正規」バージョンとして選択します。同じ数値を持つ後続のすべてのシンボルは、単なるエイリアスです。
enum はメッセージ型内にネストして定義できます。コンパイラは、Java enum 定義をそのメッセージ型のクラス内にネストして生成します。
注意: Java コードを生成する場合、protobuf enum の最大値の数は驚くほど少ない場合があります。最悪の場合、最大値はわずか 1,700 をわずかに超える値です。この制限は、Java バイトコードのメソッドごとのサイズ制限によるものであり、Java 実装、protobuf スイートの異なるバージョン、および .proto
ファイルの enum に設定されたオプションによって異なります。
拡張機能 (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);
パラメータは、method
引数が暗黙的であり、request
とdone
が正確な型を指定することを除いて、Service.CallMethod()
のパラメータと同等です。
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()
:コンストラクタに渡された、このスタブのチャネルを返します。
スタブはさらに、チャネルのラッパーとしてサービスの各メソッドを実装します。メソッドの1つを呼び出すと、単に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
は完全なproto名です。例:package.MessageType
。builder_scope:TYPENAME
:メッセージのビルダー クラスに属するメンバ宣言。TYPENAME
は完全なproto名です。例:package.MessageType
。enum_scope:TYPENAME
:enumクラスに属するメンバ宣言。TYPENAME
は完全なproto enum名です。例:package.EnumType
。message_implements:TYPENAME
:メッセージクラスのクラス実装宣言。TYPENAME
は完全なproto名です。例:package.MessageType
。builder_implements:TYPENAME
:ビルダー クラスのクラス実装宣言。TYPENAME
は完全なproto名です。例:package.MessageType
。
生成されたコードには、import文を含めることはできません。これらは、生成されたコード自体の中で定義された型名と競合する可能性があるためです。代わりに、外部クラスを参照する場合は、常にその完全修飾名を使用する必要があります。
Javaコードジェネレータで出力ファイル名を決定するロジックは非常に複雑です。すべてのケースを網羅していることを確認するために、protoc
ソースコード、特にjava_headers.cc
を確認する必要があります。
標準コードジェネレータによって宣言されたプライベートクラスメンバに依存するコードを生成しないでください。これらの実装の詳細は、Protocol Buffersの将来のバージョンで変更される可能性があるためです。
ユーティリティクラス
Protocol bufferは、メッセージ比較、JSON変換、およびwell-known types(一般的なユースケース向けに事前定義されたプロトコルバッファメッセージ)を扱うためのユーティリティクラスを提供します。