Google Protobuf 处理空值

Google Protobuf handle null values

有人向我解释如何处理 google protobuf 中的空值。
我有这样的结构:

syntax = "proto3";
import "google/protobuf/wrappers.proto";
message Person {
  google.protobuf.DoubleValue age = 4;
}     

和java代码:

DoubleValue myAge = null;
Person build = Person.newBuilder()
                .setAge(myAge)  // <- NPE
                .build();

此代码在 .setAge(myAge) 行导致空指针异常。
那么 protobuf DoubleValue 的用例是什么?我以为它是用来管理空值的。但仍然收到 NullPointerException 。

你使用这样的代码来处理这个问题吗?

DoubleValue a = null;
Person.Builder builder = Person.newBuilder();
if (a != null) {
    builder.setAge(a);
}

Person build = builder.build();     

更新:
这是 protoc 命令生成的代码的一部分:

/**
* <code>.google.protobuf.DoubleValue age = 9;</code>
*/
public Builder setAge(com.google.protobuf.DoubleValue value) {
if (ageBuilder_ == null) {
    if (value == null) {        //<-- Additional statement
        throw new NullPointerException();
    }
    age_ = value;
    onChanged();
} else {
    ageBuilder_.setMessage(value);
}   

为什么要在生成的代码中设置额外的 if 语句?
我认为这是 google 协议缓冲区的错误。
最后我写了这段代码:

DoubleValue.Builder builder = DoubleValue.newBuilder();
DoubleValue myAge = null;
Person build = Person.newBuilder()
                .setAge(myAge != null ? myAge : builder )
                .build();

DoubleValue 用于处理 缺失的 值,不一定是 null。 Protobuf 库旨在永远不涉及空值 - 例如,它永远不会 returns null,即使对于缺失的字段也是如此。此外,正如您所发现的,setters 不接受 null。例如,要清除一个字段,您可以使用 clearField 而不是 setField(null).

proto2 和 proto3 之间的一大区别是原始字段(例如 hasField)的 () 个“hazzers”被删除了。也就是说,如果您有一个 double my_field,则无法判断它是否从未设置过或明确设置为零。 getMyField() 在这两种情况下都会 return 0。

请注意,消息仍然有 hazzers,因此如果您需要该功能,可以将原始字段包装在消息中。这正是 DoubleValue is。因此,如果您有一个字段 DoubleValue age = 1,您可以使用 hasAge() 来确定它是否已设置。在较新的 protobuf 版本中,您可以使用 optional double age = 1.

执行相同的操作

请注意,所有这些都与 Java 生成的代码中的 null 无关。 setter 确实不可能接受空值。

TL;DR:您的第一个建议很好而且很常见:

DoubleValue a = null;
Person.Builder builder = Person.newBuilder();
if (a != null) {
    builder.setAge(a);
}
Person build = builder.build();     

如果你有一个 Optional 而不是 null,你可以使用类似的变体:

OptionalDouble a = OptionalDouble.empty();
Person.Builder builder = Person.newBuilder();
a.ifPresent(value-> builder.getAgeBuilder().setValue(value));

Person build = builder.build();