美文网首页JAVA进阶protobuf
使用protobuf3踩过的那些坑(Java)

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

作者: go4awalk | 来源:发表于2018-12-06 00:46 被阅读0次

    相对于 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相关操作放在数据传输的接口层。

    相关文章

      网友评论

        本文标题:使用protobuf3踩过的那些坑(Java)

        本文链接:https://www.haomeiwen.com/subject/ihogcqtx.html