Protocol Buffer JAVA实例教程:Protocol Buffer API
让我们看一下生成的代码,看看编译器为您创建了哪些类和方法。打开AddressBookProtos.java,你会发现它定义了一个名为AddressBookProtos的类,嵌套在其中的是你在addressbook.proto中指定的每条消息的一个类。每个类都有自己的Builder类,你可以用它来创建该类的实例。您可以在下面的Builders vs. Messages部分找到更多关于生成器的信息。
消息和生成器都为消息的每个字段自动生成访问者方法;消息只有获取者,而构建者既有获取者又有设置者。下面是Person类的一些访问器(为简洁起见,省略了实现):
// required string name = 1; public boolean hasName(); public String getName(); // required int32 id = 2; public boolean hasId(); public int getId(); // optional string email = 3; public boolean hasEmail(); public String getEmail(); // repeated .tutorial.Person.PhoneNumber phones = 4; public List<PhoneNumber> getPhonesList(); public int getPhonesCount(); public PhoneNumber getPhones(int index);
与此同时,erson.Builder也有同样的getter和setter:
// required string name = 1; public boolean hasName(); public java.lang.String getName(); public Builder setName(String value); public Builder clearName(); // required int32 id = 2; public boolean hasId(); public int getId(); public Builder setId(int value); public Builder clearId(); // optional string email = 3; public boolean hasEmail(); public String getEmail(); public Builder setEmail(String value); public Builder clearEmail(); // repeated .tutorial.Person.PhoneNumber phones = 4; public List<PhoneNumber> getPhonesList(); public int getPhonesCount(); public PhoneNumber getPhones(int index); public Builder setPhones(int index, PhoneNumber value); public Builder addPhones(PhoneNumber value); public Builder addAllPhones(Iterable<PhoneNumber> value); public Builder clearPhones();
如您所见,每个字段都有简单的JavaBeans风格的getter和setter。每个特殊字段也有setter,如果该字段已设置,setter、
返回真。最后,每个字段都有一个清晰的方法,可以将字段重新设置为空状态。
重复字段有一些额外的方法:计数方法(这只是列表大小的简写)、通过索引获取或设置列表中特定元素的getter和setter、向列表中添加新元素的添加方法以及向列表中添加一个装满元素的完整容器的添加所有方法。
请注意这些访问器方法如何使用camel-case命名,即.proto文件使用带下划线的小写字母。这种转换由protocol buffer编译器自动完成,以便生成的类与标准的Java风格约定相匹配。您应该始终在.proto文件中使用带下划线的小写字母作为字段名;这确保了所有生成语言的良好命名实践。
有关protocol buffer编译器为任何特定字段定义生成的确切成员的更多信息,请参见Java生成的代码引用。
枚举和内部类
生成的代码包括一个嵌套在Person中的PhoneType枚举:
public static enum PhoneType { MOBILE(0, 0), HOME(1, 1), WORK(2, 2), ; ... }
正如您所料,嵌套类型Person.PhoneNumber 是作为Person中的嵌套类生成的。
生成器 vs 消息
protocol buffer编译器生成的消息类都是不可变的。一旦消息对象被构造,就不能像Java字符串一样被修改。要构造消息,您必须首先构造一个生成器,将您想要设置的任何字段设置为您选择的值,然后调用生成器的build()方法。
您可能已经注意到,每个修改消息的生成器方法都会返回另一个生成器。返回的对象实际上是您调用方法的同一生成器。返回它是为了方便,这样您可以在一行代码中将几个setters串在一起。
下面是一个如何创建Person实例的示例:
Person john = Person.newBuilder() .setId(1234) .setName("John Doe") .setEmail("jdoe@example.com") .addPhones( Person.PhoneNumber.newBuilder() .setNumber("555-4321") .setType(Person.PhoneType.HOME)) .build();
标准消息方法
每个消息和生成器类还包含许多其他方法,可以让您检查或操作整个消息,其中包括:
isInitialized(): 检查是否所有必填字段都已设置。
toString(): 返回友好的字符串消息,对调试特别有用。
mergeFrom(Message other): (仅生成器) 将其他的内容合并到此消息中,覆盖单个标量字段,合并复合字段,并连接重复的字段。
clear(): (仅生成器) 将所有字段清除回空状态。
这些方法实现了消息和消息。所有Java消息和生成器共享的生成器接口。有关更多信息,请参见完整的消息应用编程接口文档。
解析和序列化
最后,每个协议缓冲区类都有使用协议缓冲区二进制格式写入和读取所选类型消息的方法。其中包括:
byte[] toByteArray();: 序列化消息并返回包含原始字节的字节数组。
static Person parseFrom(byte[] data);: 解析给定字节数组中的消息。
void writeTo(OutputStream output);: 序列化消息并将其写入输出流。
static Person parseFrom(InputStream input);: 读取并解析来自输入流的消息。
这些只是为解析和序列化提供的几个选项。同样,有关完整列表,请参见消息应用编程接口参考。
Protocol Buffers和O-O设计Protocol buffer类基本上是哑数据持有人(像C语言中的结构);在实物模型中,他们不是优秀的一等公民。如果您想向生成的类添加更丰富的行为,最好的方法是将生成的Protocol Buffer类包装在特定于应用程序的类中。如果您不能控制.proto文件(例如,如果您正在重用另一个项目中的一个文件)。在这种情况下,您可以使用包装类来创建一个更适合您的应用程序的独特环境的接口:隐藏一些数据和方法,公开方便的函数,等等。永远不要通过继承生成的类来给它们添加行为。这将打破内部机制,无论如何都不是好的面向对象实践。