JAVA进阶protobuf

使用protobuf3踩过的那些坑(Java)

2018-12-06  本文已影响0人  go4awalk

相对于 protobuf2,protobuf3 变化很大,尤其是默认值的变化给使用者带来很大不便。

默认值

protobuf3 删除了 protobuf2 中用来设置默认值的 default 关键字,取而代之的是protobuf3为各类型定义的默认值,也就是约定的默认值,如下表所示:

类型 默认值
bool false
整型 0
string 空字符串""
枚举enum 第一个枚举元素的值,因为Protobuf3强制要求第一个枚举元素的值必须是0,所以枚举的默认值就是0;
message 不是null,而是DEFAULT_INSTANCE

可以看出来,protobuf3定义的默认值跟Java中类的属性的默认值规则并不一样:Java中,如果类的属性类型是类,则该属性默认值是null,而protobuf3中,string、message的默认值都不是null。

枚举enum类型:

1、不支持一个proto文件中,多个枚举中定义相同的枚举常量名。

如下的两个枚举,定义在同一个proto文件中:

enum Enum1 {
    IDLE = 0;
    RUNNING = 1;
}

enum Enum2 {
    IDLE = 5;
    RUNNING = 6;
}

编译时,会报出错误:IDLE is already defined in "xxx",出现这一错误的原因就是:Protobuf3中不允许同一proto中,多个枚举中使用相同的枚举值。
而有意思的是:在proto编译生成的Java文件中,protobuf自己却为每个枚举都添加了一个UNRECOGNIZED(值为-1),意味着protobuf不允许使用者为两个枚举添加相同的枚举值,却允许自己添加。。。
实际上,这一现象是有悖于Java开发者习惯的,因为在Java中,并不会对枚举有上述限制,在使用上会让人感觉很别扭,带着这个困惑,我提了个bug:
Can not define two same enum name in the same proto file

而官方的解答是:
1、设计如此,给名字一样的枚举值加个前缀来解决这个问题。。。
2、Protobuf要兼顾所有语言的特性,我试了一下:C语言也不不允许这么定义(以前真不知道这个情况,学艺不精呀。。)。

2、枚举第一个常量的值必须是0

实际项目中使用的枚举常量值经常是从0开始的,这样项目需求与protobuf3有冲突。
解决方法是,将第一个枚举常量0定义为无效值,或者额外定义一个无效值(比如-1),Google API Guider中建议枚举的第一个值为 ENUN_TYPE_UNSPECIFIED,即枚举名_UNSPECIFIED,举个例子:

enum BallTypeEnum {
    BALL_TYPE_UNSPECIFIED = 0;
    BASKETBALL = 1;
    FOOTBALL = 2;
}

这样,让默认的枚举值与业务的相分离。

message类型:

Java中,message类型的默认值是DEFAULT_INSTANCE,其值相当于空的message,即XXX.newBuilder().build(),这样对message类型的判空操作就应该是这样:

// protobuf message
message User {
    int32 id = 1;
    string name = 2;
    string email = 3;
    Address address = 4;
}

message Address {
    string street = 1;
    string building = 2;
}

// Java
if (user.getAddress() != null && user.getAddress() != UserProto.Address.getDefaultInstance()) {
    ...
} else {
    ...
}

结语

尽管protobuf3有一些不方便的地方,但protobuf毕竟数据交换协议,负责交换数据,所以建议在使用protobuf时,不要与具体业务耦合过紧,protobuf相关操作放在数据传输的接口层。

上一篇下一篇

猜你喜欢

热点阅读