相對于 protobuf2,protobuf3 變化很大,尤其是默認值的變化給使用者帶來很大不便。
默認值
protobuf3 刪除了 protobuf2 中用來設(shè)置默認值的 default 關(guān)鍵字,取而代之的是protobuf3為各類型定義的默認值,也就是約定的默認值,如下表所示:
| 類型 | 默認值 |
|---|---|
| bool | false |
| 整型 | 0 |
| string | 空字符串"" |
| 枚舉enum | 第一個枚舉元素的值,因為Protobuf3強制要求第一個枚舉元素的值必須是0,所以枚舉的默認值就是0; |
| message | 不是null,而是DEFAULT_INSTANCE |
可以看出來,protobuf3定義的默認值跟Java中類的屬性的默認值規(guī)則并不一樣: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",出現(xiàn)這一錯誤的原因就是:Protobuf3中不允許同一proto中,多個枚舉中使用相同的枚舉值。
而有意思的是:在proto編譯生成的Java文件中,protobuf自己卻為每個枚舉都添加了一個UNRECOGNIZED(值為-1),意味著protobuf不允許使用者為兩個枚舉添加相同的枚舉值,卻允許自己添加。。。
實際上,這一現(xiàn)象是有悖于Java開發(fā)者習(xí)慣的,因為在Java中,并不會對枚舉有上述限制,在使用上會讓人感覺很別扭,帶著這個困惑,我提了個bug:
Can not define two same enum name in the same proto file
而官方的解答是:
1、設(shè)計如此,給名字一樣的枚舉值加個前綴來解決這個問題。。。
2、Protobuf要兼顧所有語言的特性,我試了一下:C語言也不不允許這么定義(以前真不知道這個情況,學(xué)藝不精呀。。)。
2、枚舉第一個常量的值必須是0
實際項目中使用的枚舉常量值經(jīng)常是從0開始的,這樣項目需求與protobuf3有沖突。
解決方法是,將第一個枚舉常量0定義為無效值,或者額外定義一個無效值(比如-1),Google API Guider中建議枚舉的第一個值為 ENUN_TYPE_UNSPECIFIED,即枚舉名_UNSPECIFIED,舉個例子:
enum BallTypeEnum {
BALL_TYPE_UNSPECIFIED = 0;
BASKETBALL = 1;
FOOTBALL = 2;
}
這樣,讓默認的枚舉值與業(yè)務(wù)的相分離。
message類型:
Java中,message類型的默認值是DEFAULT_INSTANCE,其值相當(dāng)于空的message,即XXX.newBuilder().build(),這樣對message類型的判空操作就應(yīng)該是這樣:
// 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 {
...
}
結(jié)語
盡管protobuf3有一些不方便的地方,但protobuf畢竟數(shù)據(jù)交換協(xié)議,負責(zé)交換數(shù)據(jù),所以建議在使用protobuf時,不要與具體業(yè)務(wù)耦合過緊,protobuf相關(guān)操作放在數(shù)據(jù)傳輸?shù)慕涌趯印?/p>