google protobuf 生成的JAVA语言对象结构,介绍 .proto JAVA语言对象中的方法
小标 2018-09-11 来源 : 阅读 785 评论 0

摘要:本文主要向大家介绍了google protobuf 生成的JAVA语言对象结构,介绍 .proto JAVA语言对象中的方法,通过具体的内容向大家展示,希望对大家学习JAVA语言有所帮助。

本文主要向大家介绍了google protobuf 生成的JAVA语言对象结构,介绍 .proto JAVA语言对象中的方法,通过具体的内容向大家展示,希望对大家学习JAVA语言有所帮助。

介绍

介绍日后再补TODO,总之想要了解protobuf的工作原理,需要首先知道编程中的“Builder模式”——由于类属性过多而出现的一种较好的解决方式。没有公有构造函数,设置属性仅能通过Builder的set类方法等等。具体可查阅资料学习。

这里介绍一下protobuf生成的Java对象结构。

.proto

假设protobuf定义文件如下:

option java_outer_classname = "Test";message A {

   required string a = 1;    message B1 {

       optional string b = 1;        message C {

           optional string c = 1;
       }
       optional C c = 2;
   }
   optional B1 b1 = 2;    message B2 {
       optional int32 b = 1;
   }
   optional B2 b2 = 3;

   optional int32 num = 4;
}12345678910111213141516171819202122232425

则会生成类名为Test.java的文件,结构和protobuf文件定义的一致:A对象,包含a/b1/b2/num四个属性,其中b1类型为B1,b2类型为B2;B1中包含b/c两个属性,其中c类型为C,其中只包含一个属性c;B2只包含一个属性b。

Java对象中的方法

主要看一下生成的对象中,都有哪些方法可供使用。由于生成的Java文件太大,就不再贴上来了。

methods in interface

首先,针对最外层,会生成AOrBuilder接口。类A会实现该接口,类A的静态内部类(或者说静态嵌套类)A.Builder也会实现该接口。

接口中包含的方法基本都是获取各个属性的—— 
比如对于A的String类型属性a,有: 
- hasA(),判断属性a是否存在值; 
- getA(),获取属性a的值; 
- getABytes(),仅针对string类型的属性,如果a是int型,自然不会有此方法。

如果属性是结构体型的,比如对于b1,其类型为B1,那么接口中会有getB1OrBuilder()方法。如果b1有值,会返回B1,否则返回B1.Builder。

methods in object

对于object,比如类型A,主要有以下几种类型的方法——

object本身的特性: 
- isInitialized(),判断required属性是否设置了,如果没有则抛异常; 
- getSerializedSize(),获取序列化后的大小; 
- getDefaultInstance(),工厂方法,获得未初始化的类实例,相当于Test.A.newBuilder().build();

对属性的get方法: 
这一点是对上述接口中方法的实现。比如:hasA()/getA()/getABytes()/getB1OrBuilder(),详见对接口方法的介绍。

转换方法: 
- newBuilder(),新建一个Builder对象,这也是构造对象的最常用方式,毕竟没有公有构造函数; 
- newBuilder(A object),重载方法,新建一个Builder对象,并将属性值设置和和传入的object相同。由于内嵌的结构体(比如B1类型的对象)和string对象是不可变的,因此他们是在原对象和拷贝对象之间共享的(不可变对象可以安全地共享); 
- toBuilder(),将对象转换为Builder,返回的是深拷贝,对该Builder的属性的修改不会影响到原对象; 
- parseFrom(ByteString/byte[]/InputStream),从一堆字节中解析出对象。和其Builder中的mergeFrom(A object)方法一一对应; 
转换方法是比较重要的。

object对象是不可变的,无法做到对属性的值进行删改,只能查询。删改只有通过Builder才能做到。

methods in Builder

对于Builder,比如A.Builder,主要有以下几种类型的方法——

查询属性: 
同上述接口中介绍的方法,毕竟Builder实现了上述接口。比如:hasA()/getA()/getABytes()/getB1OrBuilder()

删改属性。比如对于A的属性a,有: 
- setA(String),set属性a的值; 
- setABytes(ByteString),同上,自然也只有当a为string的时候才会有此方法; 
- clearA(),清除a的值; 
如果属性是复杂的结构体,比如对于属性b1,还存在如下删改方法: 
- setB1(B1)/setB1(B1.Builder),前者没什么特别之处,但是后者作为重载方法,可以使用B1.Builder类型的对象直接setB1类型的对象的值; 
- mergeB1(B1),同样是设置b1的值,区别:TODO

转换方法: 
- build(),将Builder转为object,如果required属性没有设置值,抛异常; 
- builderPartial(),同样将Builder转为object,但是required属性没有设置值也不会有问题; 
- mergeFrom(A),直接从一个现有的A对象,深拷贝其属性,构造一个全新的A.Builder对象;

Builder本身的一些方法: 
- clone() 
- clear()

最值得注意的是,A.Builder中含有便捷的方法,能够直接获取Builder中嵌套的结构体的Builder,即“使用builder直接获取深层builder”。

比如给出A.Builder,想要修改其中的属性b1的属性b:

aBuilder.getB1Builder().setB("convenint_set")1

就完成了对值的修改。

否则,需要这样去修改:

Test.A.B1.Builder b1Builder = aBuilder.getB1().toBuilder().setB("change_only_if_set_again");aBuilder.setB1(b1Builder); // 使用b1或b1Builder都行,有重载方法12

先获取A.Builder中的B1对象,再转B1.Builder,最后set属性b;

再把新对象设置回A.Builder。否则原aBuilder是不会改变的。

所以使用builder获取深层次builder并修改,是非常方便的,尤其是在嵌套层级比较深的情况下。

再比如,想要修改其中的属性b1的属性c的属性c,只需:

aBuilder.getB1Builder().getCBuilder().setC("builder_get_builder")1

就完成了对值的修改。

示例

/**
* @author liuhaibo on 2018/06/27
*/public class Main {

   public static void main(String... args) {
       Test.A.B1.C cObject = Test.A.B1.C.newBuilder().setC("c").build();
       Test.A.B1 b1Object = Test.A.B1.newBuilder().setB("b").setC(cObject).build();
       Test.A.B2 b2Object = Test.A.B2.newBuilder().setB(1).build();
       Test.A aObject = Test.A.newBuilder().setA("a").setB1(b1Object).setB2(b2Object).build();

       System.out.println("///* Methods for object *///");
       methodsForObject(aObject);

       System.out.println("///* Methods for Builder *///");
       methodsForBuilder(aObject, b1Object);
   }

   private static void methodsForObject(Test.A aObject) {
       System.out.println("A = " + aObject);
       System.out.println("A.isInitialized() = " + aObject.isInitialized());

       // Exception in thread "main" com.google.protobuf.UninitializedMessageException: Message missing required fields: a
       // System.out.println("A(without a field).isInitialized() = " + aObject.toBuilder().clearA().build().isInitialized());

       System.out.println("A.getSerializedSize() = " + aObject.getSerializedSize());
       System.out.println("A.Builder = " + aObject.toBuilder());
       System.out.println("A.getB1OrBuilder() = " + aObject.getB1OrBuilder());
       System.out.println("A.b1 = " + aObject.getB1());
   }

   private static void methodsForBuilder(Test.A aObject, Test.A.B1 b1Object) {
       // builder
       Test.A.Builder aBuilder = Test.A.newBuilder();

       // merge from a existing object
       aBuilder.mergeFrom(aObject);
       System.out.println(aBuilder);

       // change a structure in object
       aBuilder.mergeB1(b1Object.toBuilder().setB("change_a_structure").build());
       System.out.println(aBuilder);

       // 用builder去get builder,需要修改值直接set就可以了
       aBuilder.getB1Builder().getCBuilder().setC("builder_get_builder").build();
       System.out.println(aBuilder);

       // 用object去toBuilder,再set值,那么原本的object不会变
       aBuilder.getB1().toBuilder().setB("no_change_happens");
       System.out.println(aBuilder);
       // 除非再set回去
       Test.A.B1.Builder b1Builder = aBuilder.getB1().toBuilder().setB("change_only_if_set_again");
       aBuilder.setB1(b1Builder); // 使用b1或b1Builder都行,有重载方法
       System.out.println(aBuilder);

       // buildPartial() vs. build()
       aBuilder.clearA().getB1Builder().getCBuilder().setC("2333"); // 现在已经是CBuilder对象了,不再是ABuilder,所以调用build()也是CBuilder的方法
       // safe
       System.out.println(aBuilder.buildPartial());
       aBuilder.clearA();
       // Exception in thread "main" com.google.protobuf.UninitializedMessageException: Message missing required fields: a
       // System.out.println(aBuilder.build());
   }
}

本文由职坐标整理并发布,希望对同学们有所帮助。了解更多详情请关注编程语言JAVA频道!

本文由 @小标 发布于职坐标。未经许可,禁止转载。
喜欢 | 1 不喜欢 | 0
看完这篇文章有何感觉?已经有1人表态,100%的人喜欢 快给朋友分享吧~
评论(0)
后参与评论

您输入的评论内容中包含违禁敏感词

我知道了

助您圆梦职场 匹配合适岗位
验证码手机号,获得海同独家IT培训资料
选择就业方向:
人工智能物联网
大数据开发/分析
人工智能Python
Java全栈开发
WEB前端+H5

请输入正确的手机号码

请输入正确的验证码

获取验证码

您今天的短信下发次数太多了,明天再试试吧!

提交

我们会在第一时间安排职业规划师联系您!

您也可以联系我们的职业规划师咨询:

小职老师的微信号:z_zhizuobiao
小职老师的微信号:z_zhizuobiao

版权所有 职坐标-一站式IT培训就业服务领导者 沪ICP备13042190号-4
上海海同信息科技有限公司 Copyright ©2015 www.zhizuobiao.com,All Rights Reserved.
 沪公网安备 31011502005948号    

©2015 www.zhizuobiao.com All Rights Reserved

208小时内训课程