OBSERVAÇÃO:este site foi descontinuado. O site será desativado após 31 de janeiro de 2023, e o tráfego será redirecionado para o novo site em https://protobuf.dev. Enquanto isso, as atualizações serão feitas apenas para protobuf.dev.

Código gerado por Java

Mantenha tudo organizado com as coleções Salve e categorize o conteúdo com base nas suas preferências.

Esta página descreve exatamente qual código Java o compilador de buffer de protocolo gera para qualquer definição de protocolo. Qualquer diferença entre o código gerado por proto2 e proto3 está destacada. Observe que essas diferenças estão no código gerado conforme descrito neste documento, não nas classes/interfaces de mensagem base, que são as mesmas nas duas versões. Leia o guia de idiomas do proto2 e/ou o guia do idioma do proto3 antes de ler este documento.

Observe que nenhum método Java do buffer de protocolo aceita ou retorna nulos, a menos que seja especificado de outra forma.

Invocação do compilador

O compilador de buffer de protocolo produz a saída Java quando invocada com a sinalização de linha de comando --java_out=. O parâmetro para a opção --java_out= é o diretório em que você quer que o compilador grave a saída do Java. Para cada entrada de arquivo .proto, o compilador cria um arquivo .java wrapper que contém uma classe Java que representa o próprio arquivo .proto.

Se o arquivo .proto contiver uma linha como a seguinte:

option java_multiple_files = true;

Em seguida, o compilador também cria um arquivo .java separado para cada mensagem de nível superior, enumeração e serviço declarados no arquivo .proto.

Caso contrário, quando a opção java_multiple_files for "false" (o padrão), a classe de wrapper mencionada acima também será usada como uma classe externa, e as classes/enumerações geradas para cada mensagem de nível superior, enumeração e serviço declaradas no arquivo .proto serão aninhadas dentro da classe de wrapper externo. Assim, o compilador só gera um único arquivo .java para todo o arquivo .proto.

O nome da classe de wrapper é escolhido da seguinte maneira. Se o arquivo .proto contiver uma linha como a seguinte:

option java_outer_classname = "Foo";

O nome da classe de wrapper será Foo. Caso contrário, o nome da classe do wrapper é determinado pela conversão do nome base do arquivo .proto em letras concatenadas. Por exemplo, foo_bar.proto gerará um nome de classe de FooBar. Se houver um serviço, uma enumeração ou uma mensagem (incluindo tipos aninhados) no arquivo com o mesmo nome, "OuterClass" vai ser anexado ao nome da classe do wrapper. Exemplos:

  • Se foo_bar.proto contiver uma mensagem chamada FooBar, a classe de wrapper gerará um nome de classe de FooBarOuterClass.
  • Se foo_bar.proto contiver um serviço chamado FooService e java_outer_classname também estiver definido como a string FooService, a classe de wrapper gerará um nome de classe de FooServiceOuterClass.

Além de quaisquer classes aninhadas, a própria classe do wrapper terá a seguinte API, supondo que a classe do wrapper seja chamada Foo e tenha sido gerada de foo.proto:

public final class Foo {
  private Foo() {}  // Not instantiable.

  /** Returns a FileDescriptor message describing the contents of {@code foo.proto}. */
  public static com.google.protobuf.Descriptors.FileDescriptor getDescriptor();
  /** Adds all extensions defined in {@code foo.proto} to the given registry. */
  public static void registerAllExtensions(com.google.protobuf.ExtensionRegistry registry);
  public static void registerAllExtensions(com.google.protobuf.ExtensionRegistryLite registry);

  // (Nested classes omitted)
}

O nome do pacote Java é escolhido conforme descrito em Pacotes abaixo.

O arquivo de saída é escolhido concatenando o parâmetro para --java_out=, o nome do pacote (com .s substituídos por /s) e o nome do arquivo .java.

Então, por exemplo, digamos que você invoque o compilador da seguinte maneira:

protoc --proto_path=src --java_out=build/gen src/foo.proto

Se o pacote Java do foo.proto for com.example e ele não ativar java_multiple_files e o nome de classe externo dele for FooProtos, o compilador de buffer de protocolo gerará o arquivo build/gen/com/example/FooProtos.java. O compilador de buffer de protocolo criará automaticamente os diretórios build/gen/com e build/gen/com/example, se necessário. No entanto, ela não criará a build/gen ou a build. É necessário que elas já existam. É possível especificar vários arquivos .proto em uma única invocação. Todos os arquivos de saída serão gerados de uma só vez.

Ao gerar código Java, a capacidade do compilador de buffer de protocolo de saída diretamente para arquivos JAR é particularmente conveniente, porque muitas ferramentas Java são capazes de ler código-fonte diretamente de arquivos JAR. Para gerar um arquivo JAR, forneça um local de saída terminado em .jar. Apenas o código-fonte Java é colocado no arquivo. É necessário compilá-lo separadamente para produzir arquivos de classe Java.

Entrega de pacotes

A classe gerada é colocada em um pacote Java com base na opção java_package. Se a opção for omitida, a declaração package será usada.

Por exemplo, se o arquivo .proto contiver:

package foo.bar;

Em seguida, a classe Java resultante será colocada no pacote Java foo.bar. No entanto, se o arquivo .proto também tiver uma opção java_package, como:

package foo.bar;
option java_package = "com.example.foo.bar";

Em seguida, a classe é colocada no pacote com.example.foo.bar. A opção java_package é fornecida porque declarações .proto package normais não devem começar com um nome de domínio reverso.

Mensagens

Se a declaração for simples:

message Foo {}

O compilador de buffer de protocolo gera uma classe chamada Foo, que implementa a interface Message. A classe é declarada como final. Nenhuma outra subclasse é permitida. Foo estende GeneratedMessage, mas isso deve ser considerado um detalhe de implementação. Por padrão, o Foo modifica muitos métodos de GeneratedMessage por versões especializadas para alcançar a velocidade máxima. No entanto, se o arquivo .proto contiver a linha:

option optimize_for = CODE_SIZE;

o Foo vai substituir apenas o conjunto mínimo de métodos necessários para funcionar e confiar nas implementações baseadas em reflexão do GeneratedMessage do restante. Isso reduz significativamente o tamanho do código gerado, mas também reduz o desempenho.

Como alternativa, se o diretório de saída Java usado pelo protoc for prefixado com lite:, por exemplo, --java_out=lite:project/protos, Foo incluirá implementações rápidas de todos os métodos, mas implementará a interface MessageLite, que contém apenas um subconjunto dos métodos de Message. Mais especificamente, ele não é compatível com descritores ou reflexão. No entanto, nesse modo, o código gerado só precisa ser vinculado a libprotobuf-lite.jar em vez de libprotobuf.jar. A biblioteca "lite" é muito menor que a biblioteca completa e é mais adequada para sistemas com recursos limitados, como smartphones.

A interface Message define métodos que permitem verificar, manipular, ler ou gravar a mensagem inteira. Além desses métodos, a classe Foo define os seguintes métodos estáticos:

  • static Foo getDefaultInstance(): retorna a instância singleton de Foo. O conteúdo dessa instância será idêntico ao que você receberia se chamasse Foo.newBuilder().build(). Assim, todos os campos únicos não estão definidos e todos os campos repetidos estão vazios. A instância padrão de uma mensagem pode ser usada como fábrica chamando o método newBuilderForType().
  • static Descriptor getDescriptor(): retorna o descritor do tipo. Ela contém informações sobre o tipo, incluindo quais campos ele tem e quais são os tipos. Isso pode ser usado com os métodos de reflexão do Message, como getField().
  • static Foo parseFrom(...): analisa uma mensagem do tipo Foo da origem fornecida e a retorna. Há um método parseFrom correspondente a cada variante de mergeFrom() na interface Message.Builder. A parseFrom() nunca gera UninitializedMessageException. Ela gera InvalidProtocolBufferException se a mensagem analisada não tem campos obrigatórios. Isso o torna um pouco diferente da chamada de Foo.newBuilder().mergeFrom(...).build().
  • static Parser parser(): retorna uma instância do Parser, que implementa vários métodos parseFrom().
  • Foo.Builder newBuilder(): cria um novo builder (descrito abaixo).
  • Foo.Builder newBuilder(Foo prototype): cria um novo builder com todos os campos inicializados com os mesmos valores que em prototype. Como os objetos de string e mensagem incorporados são imutáveis, eles são compartilhados entre o original e a cópia.

Criadores

Os objetos de mensagem, como instâncias da classe Foo descrita acima, são imutáveis, assim como um String do Java. Para criar um objeto de mensagem, você precisa usar um builder. Cada classe de mensagem tem a própria classe builder. Portanto, no exemplo do Foo, o compilador do buffer de protocolo gera uma classe aninhada Foo.Builder, que pode ser usada para criar uma Foo. Foo.Builder implementa a interface Message.Builder. Ela estende a classe GeneratedMessage.Builder, mas, mais uma vez, isso deve ser considerado um detalhe de implementação. Assim como a Foo, a Foo.Builder pode depender de implementações de método genérico na GeneratedMessage.Builder ou, quando a opção optimize_for é usada, gerar um código personalizado muito mais rápido.

Foo.Builder não define métodos estáticos. A interface dela é exatamente como definido pela interface Message.Builder, com a exceção de que os tipos de retorno são mais específicos: métodos de Foo.Builder que modificam o tipo de retorno do builder Foo.Builder e build() retorna o tipo Foo.

Os métodos que modificam o conteúdo de um builder, incluindo setters de campo, sempre retornam uma referência ao builder. Ou seja, "return this;". Isso permite que várias chamadas de método sejam encadeadas em uma linha. Por exemplo: builder.mergeFrom(obj).setFoo(1).setBar("abc").clearBaz();

Como os builders não são seguros para linhas de execução, a sincronização Java precisa ser usada sempre que for necessário que várias linhas de execução diferentes modifiquem o conteúdo de um único builder.

Subcriadores

Para mensagens que contêm submensagens, o compilador também gera subbuilders. Isso permite modificar repetidamente as submensagens aninhadas sem recriá-las. Por exemplo:

message Foo {
  optional int32 val = 1;
  // some other fields.
}

message Bar {
  optional Foo foo = 1;
  // some other fields.
}

message Baz {
  optional Bar bar = 1;
  // some other fields.
}

Se você já tem uma mensagem Baz e quer mudar o val aninhado em Foo. Em vez de:

  baz = baz.toBuilder().setBar(
      baz.getBar().toBuilder().setFoo(
          baz.getBar().getFoo().toBuilder().setVal(10).build()
      ).build()).build();

É possível escrever:

  Baz.Builder builder = baz.toBuilder();
  builder.getBarBuilder().getFooBuilder().setVal(10);
  baz = builder.build();

Tipos aninhados

Uma mensagem pode ser declarada dentro de outra. Por exemplo: message Foo { message Bar { } }

Nesse caso, o compilador simplesmente gera Bar como uma classe interna aninhada em Foo.

Campos

Além dos métodos descritos na seção anterior, o compilador de buffer de protocolo gera um conjunto de métodos do acessador para cada campo definido na mensagem no arquivo .proto. Os métodos que leem o valor do campo são definidos tanto na classe da mensagem quanto no builder correspondente. Os métodos que modificam o valor são definidos apenas no builder.

Os nomes de métodos sempre usam letras concatenadas, mesmo que o nome do campo no arquivo .proto use letras minúsculas com sublinhados (como deve ser feito). A conversão de caso funciona da seguinte maneira:

  1. Para cada sublinhado no nome, o sublinhado é removido e a letra a seguir é maiúscula.
  2. Se o nome tiver um prefixo como "get", a primeira letra é maiúscula. Caso contrário, ela está em letras minúsculas.

Assim, o campo foo_bar_baz se torna fooBarBaz. Se prefixado com get, seria getFooBarBaz.

Em alguns casos especiais, em que um nome de método entra em conflito com palavras reservadas em Java ou métodos já definidos na biblioteca protobuf, um sublinhado extra é anexado. Por exemplo, o getter de um campo chamado class é getClass_ para evitar conflito com o método getClass em java.lang.Object.

Além dos métodos de acessador, o compilador gera uma constante de número inteiro para cada campo que contém o número do campo. O nome da constante é o nome do campo convertido em maiúsculas e depois _FIELD_NUMBER. Por exemplo, dado o campo optional int32 foo_bar = 5;, o compilador vai gerar a constante public static final int FOO_BAR_FIELD_NUMBER = 5;.

Campos Singular (proto2)

Para qualquer uma dessas definições de campo:

optional int32 foo = 1;
required int32 foo = 1;

O compilador vai gerar os seguintes métodos de acessado na classe da mensagem e no builder:

  • boolean hasFoo(): retorna true se o campo estiver definido.
  • int getFoo(): retorna o valor atual do campo. Se o campo não for definido, retornará o valor padrão.

O compilador vai gerar os seguintes métodos apenas no builder da mensagem:

  • Builder setFoo(int value): define o valor do campo. Depois disso, hasFoo() retornará true, e getFoo() retornará value.
  • Builder clearFoo(): limpa o valor do campo. Depois de chamar isso, hasFoo() retornará false, e getFoo() retornará o valor padrão.

Para outros tipos de campo simples, o tipo Java correspondente é escolhido de acordo com a tabela de tipos de valores escalares. Nos tipos de mensagem e enum, o tipo de valor é substituído pela mensagem ou pela classe de enumeração.

Campos de mensagens incorporados

Para tipos de mensagem, setFoo() também aceita uma instância do tipo de criador da mensagem como parâmetro. Esse é apenas um atalho equivalente a chamar .build() no builder e transmitir o resultado para o método.

Se o campo não for definido, getFoo() retornará uma instância de Foo sem um dos campos definidos (possivelmente a instância retornada por Foo.getDefaultInstance()).

Além disso, o compilador gera dois métodos de acesso que permitem acessar os subbuilders relevantes para os tipos de mensagem. O método a seguir é gerado na classe da mensagem e no builder:

  • FooOrBuilder getFooOrBuilder(): retorna o builder do campo, se já existir, ou a mensagem, se não existir. Chamar esse método em builders não criará um subbuilder para o campo.

O compilador gera o seguinte método apenas no builder da mensagem.

  • Builder getFooBuilder(): retorna o builder do campo.

Campos Singular (proto3)

Para esta definição de campo:

int32 foo = 1;

O compilador vai gerar o seguinte método de acessado na classe da mensagem e no builder:

  • int getFoo(): retorna o valor atual do campo. Se o campo não estiver definido, retorna o valor padrão para o tipo de campo.

O compilador vai gerar os seguintes métodos apenas no builder da mensagem:

  • Builder setFoo(int value): define o valor do campo. Depois de chamar isso, getFoo() retornará value.
  • Builder clearFoo(): limpa o valor do campo. Depois de chamar isso, getFoo() retornará o valor padrão para o tipo de campo.

Para outros tipos de campo simples, o tipo Java correspondente é escolhido de acordo com a tabela de tipos de valores escalares. Nos tipos de mensagem e enum, o tipo de valor é substituído pela mensagem ou pela classe de enumeração.

Campos de mensagens incorporados

Para tipos de campo de mensagem, um método de acessador adicional é gerado na classe de mensagem e no builder:

  • boolean hasFoo(): retornará true se o campo tiver sido definido.

setFoo() também aceita uma instância do tipo de builder da mensagem como parâmetro. Esse é apenas um atalho equivalente a chamar .build() no builder e transmitir o resultado para o método.

Se o campo não for definido, getFoo() retornará uma instância de Foo sem um dos campos definidos (possivelmente a instância retornada por Foo.getDefaultInstance()).

Além disso, o compilador gera dois métodos de acesso que permitem acessar os subbuilders relevantes para os tipos de mensagem. O método a seguir é gerado na classe da mensagem e no builder:

  • FooOrBuilder getFooOrBuilder(): retorna o builder do campo, se já existir, ou a mensagem, se não existir. Chamar esse método em builders não criará um subbuilder para o campo.

O compilador gera o seguinte método apenas no builder da mensagem.

  • Builder getFooBuilder(): retorna o builder do campo.

Campos de enumeração

Para tipos de campo de enumeração, um método do acessador adicional é gerado na classe da mensagem e no builder:

  • int getFooValue(): retorna o valor inteiro da enumeração.

O compilador vai gerar o seguinte método adicional somente no builder da mensagem:

  • Builder setFooValue(int value): define o valor inteiro da enumeração.

Além disso, getFoo() retornará UNRECOGNIZED se o valor da enumeração for desconhecido. Esse é um valor adicional especial adicionado pelo compilador proto3 ao tipo de enumeração gerado.

Campos repetidos

Para esta definição de campo:

repeated string foo = 1;

O compilador vai gerar os seguintes métodos de acessado na classe da mensagem e no builder:

  • int getFooCount(): retorna o número de elementos atualmente em campo.
  • String getFoo(int index): retorna o elemento no índice com base em zero especificado.
  • ProtocolStringList getFooList(): retorna todo o campo como ProtocolStringList. Se o campo não for definido, retornará uma lista vazia.

O compilador vai gerar os seguintes métodos apenas no builder da mensagem:

  • Builder setFoo(int index, String value): define o valor do elemento no índice baseado em zero específico.
  • Builder addFoo(String value): anexa um novo elemento ao campo com o valor informado.
  • Builder addAllFoo(Iterable<? extends String> value): anexa todos os elementos no Iterable especificado ao campo.
  • Builder clearFoo(): remove todos os elementos do campo. Depois de chamar isso, getFooCount() retornará zero.

Para outros tipos de campo simples, o tipo Java correspondente é escolhido de acordo com a tabela de tipos de valores escalares. Para mensagens e tipos de enumeração, o tipo é a mensagem ou a classe de enumeração.

Campos repetidos de mensagem incorporada

Para tipos de mensagem, setFoo() e addFoo() também aceitam uma instância do tipo de builder da mensagem como o parâmetro. Esse é apenas um atalho equivalente a chamar .build() no builder e transmitir o resultado para o método. Há também um método gerado adicional:

  • Builder addFoo(int index, Field value): insere um novo elemento no índice baseado em zero específico. Desloca o elemento que está atualmente naquela posição (se houver) e os elementos subsequentes para a direita (adiciona um ao índice da pessoa).

Além disso, o compilador gera os seguintes métodos extras de acesso na classe da mensagem e no builder para tipos de mensagem, permitindo que você acesse os subconstrutores relevantes:

  • FooOrBuilder getFooOrBuilder(int index): retorna o builder do elemento especificado, se já existir, ou o elemento em caso negativo. Se for chamada em uma classe de mensagem, ela sempre retornará uma mensagem em vez de um builder. Chamar esse método em builders não criará um subbuilder para o campo.
  • List<FooOrBuilder> getFooOrBuilderList(): retorna todo o campo como uma lista não modificável de builders (se disponível) ou mensagens se não estiver. Se ela for chamada em uma classe de mensagem, ela sempre retornará uma lista imutável de mensagens em vez de uma lista não modificável de builders.

O compilador vai gerar os seguintes métodos apenas no builder da mensagem:

  • Builder getFooBuilder(int index): retorna o builder do elemento no índice especificado.
  • Builder addFooBuilder(int index): insere e retorna um builder para uma instância de mensagem padrão no índice especificado. As entradas atuais são movidas para índices mais altos a fim de criar espaço para o builder inserido.
  • Builder addFooBuilder(): anexa e retorna um builder para uma instância de mensagem padrão.
  • Builder removeFoo(int index): remove o elemento no índice baseado em zero específico.
  • List<Builder> getFooBuilderList(): retorna todo o campo como uma lista não modificável de builders.

Campos de enumeração repetidos (somente proto3)

O compilador vai gerar os seguintes métodos adicionais na classe da mensagem e no builder:

  • int getFooValue(int index): retorna o valor inteiro da enumeração no índice especificado.
  • List<java.lang.Integer> getFooValueList(): retorna o campo inteiro como uma lista de números inteiros.

O compilador vai gerar o seguinte método adicional somente no builder da mensagem:

  • Builder setFooValue(int index, int value): define o valor inteiro da enumeração no índice especificado.

Conflitos de nomes

Se outro campo não repetido tiver um nome conflitante com um dos métodos gerados, os dois nomes terão o número protobuf anexado ao final.

Para estas definições de campo:

int32 foo_count = 1;
repeated string foo = 2;

Primeiro, o compilador os renomeará da seguinte forma:

int32 foo_count_1 = 1;
repeated string foo_2 = 2;
Os métodos do acessador serão gerados conforme descrito acima.

Oneof Fields

Para esta definição de campo única:

oneof example_name {
    int32 foo = 1;
    ...
}

O compilador vai gerar os seguintes métodos de acessado na classe da mensagem e no builder:

  • boolean hasFoo() (somente proto2): retorna true se o um dos casos for FOO.
  • int getFoo(): retorna o valor atual de example_name se o caso único for FOO. Caso contrário, retorna o valor padrão deste campo.

O compilador vai gerar os seguintes métodos apenas no builder da mensagem:

  • Builder setFoo(int value): define example_name como esse valor e define o único caso como FOO. Depois disso, hasFoo() retornará true, getFoo() retornará value e getExampleNameCase() retornará FOO.
  • Builder clearFoo():
    • Nada vai mudar se o caso não for FOO.
    • Se o caso um for FOO, vai definir example_name como nulo e o único como EXAMPLENAME_NOT_SET. Depois de chamar esse método, hasFoo() retornará false, getFoo() retornará o valor padrão e getExampleNameCase() retornará EXAMPLENAME_NOT_SET.

Para outros tipos de campo simples, o tipo Java correspondente é escolhido de acordo com a tabela de tipos de valores escalares. Nos tipos de mensagem e enum, o tipo de valor é substituído pela mensagem ou pela classe de enumeração.

Campos do mapa

Para esta definição de campo do mapa:

map<int32, int32> weight = 1;

O compilador vai gerar os seguintes métodos de acessado na classe da mensagem e no builder:

  • Map<Integer, Integer> getWeightMap();: retorna um Map não modificável.
  • int getWeightOrDefault(int key, int default);: retorna o valor da chave ou o valor padrão, se ausente.
  • int getWeightOrThrow(int key);: retorna o valor da chave ou gera uma IllegalArgumentException se não houver presença.
  • boolean containsWeight(int key);: indica se a chave está presente nesse campo.
  • int getWeightCount();: retorna o número de elementos no mapa.

O compilador vai gerar os seguintes métodos apenas no builder da mensagem:

  • Builder putWeight(int key, int value);: adicione o peso a este campo.
  • Builder putAllWeight(Map<Integer, Integer> value);: adiciona todas as entradas no mapa a este campo.
  • Builder removeWeight(int key);: remove o peso deste campo.
  • Builder clearWeight();: remove todos os pesos deste campo.
  • @Deprecated Map<Integer, Integer> getMutableWeight();: retorna um Map mutável. Várias chamadas para esse método podem retornar diferentes instâncias de mapa. A referência do mapa retornada pode ser invalidada por qualquer chamada de método subsequente para o Builder.

Qualquer

Dado um campo Any como este:

import "google/protobuf/any.proto";

message ErrorStatus {
  string message = 1;
  google.protobuf.Any details = 2;
}

No código gerado, o getter para o campo details retorna uma instância de com.google.protobuf.Any. Isso fornece os seguintes métodos especiais para empacotar e descompactar os valores de Any:

class Any {
  // Packs the given message into an Any using the default type URL
  // prefix “type.googleapis.com”.
  public static Any pack(Message message);
  // Packs the given message into an Any using the given type URL
  // prefix.
  public static Any pack(Message message,
                         String typeUrlPrefix);

  // Checks whether this Any message’s payload is the given type.
  public <T extends Message> boolean is(class<T> clazz);

  // Unpacks Any into the given message type. Throws exception if
  // the type doesn’t match or parsing the payload has failed.
  public <T extends Message> T unpack(class<T> clazz)
      throws InvalidProtocolBufferException;
}

Oneof

Considere uma das seguintes definições:

oneof example_name {
    int32 foo_int = 4;
    string foo_string = 9;
    ...
}

Todos os campos na example_name usarão um único campo particular como valor. Além disso, o compilador de buffer de protocolo gerará um tipo de enumeração Java para o caso um, da seguinte maneira:

public enum ExampleNameCase
        implements com.google.protobuf.Internal.EnumLite {
      FOO_INT(4),
      FOO_STRING(9),
      ...
      EXAMPLENAME_NOT_SET(0);
      ...
    };

Os valores desse tipo de enumeração têm os seguintes métodos especiais:

  • int getNumber(): retorna o valor numérico do objeto, conforme definido no arquivo .proto.
  • static ExampleNameCase forNumber(int value): retorna o objeto enum correspondente ao valor numérico fornecido (ou null para outros valores numéricos).

O compilador também vai gerar o seguinte método de acessado na classe da mensagem e no builder:

  • ExampleNameCase getExampleNameCase(): retorna a enumeração indicando qual campo está definido. Retornará EXAMPLENAME_NOT_SET se nenhum deles estiver definido.

O compilador vai gerar o seguinte método apenas no builder da mensagem:

  • Builder clearExampleName(): redefine o campo particular oneof como nulo e o define como EXAMPLENAME_NOT_SET.

Enumerações

Considerando uma definição de enumeração como:

enum Foo {
  A = 0;
  B = 5;
  C = 1234;
}

O compilador de buffer de protocolo gerará um tipo de enumeração Java chamado Foo com o mesmo conjunto de valores. Se você estiver usando o proto3, ele também adicionará o valor especial UNRECOGNIZED ao tipo de enumeração. Os valores do tipo de enumeração gerado têm os seguintes métodos especiais:

  • int getNumber(): retorna o valor numérico do objeto, conforme definido no arquivo .proto.
  • EnumValueDescriptor getValueDescriptor(): retorna o descritor do valor, que contém informações sobre nome, número e tipo.
  • EnumDescriptor getDescriptorForType(): retorna o descritor do tipo enum, que contém informações sobre cada valor definido.

Além disso, o tipo de enumeração Foo contém os seguintes métodos estáticos:

  • static Foo forNumber(int value): retorna o objeto enum correspondente ao valor numérico fornecido. Retorna nulo quando não há um objeto de enumeração correspondente.
  • static Foo valueOf(int value): retorna o objeto enum correspondente ao valor numérico fornecido. Esse método foi descontinuado e substituído por forNumber(int value). Ele será removido em uma versão futura.
  • static Foo valueOf(EnumValueDescriptor descriptor): retorna o objeto enum correspondente ao descritor de valor fornecido. Pode ser mais rápido que valueOf(int). No proto3, retorna UNRECOGNIZED se receber um descritor de valor desconhecido.
  • EnumDescriptor getDescriptor(): retorna o descritor do tipo enum, que contém informações sobre cada valor definido. Isso difere de getDescriptorForType(), porque é um método estático.

Uma constante de número inteiro também é gerada com o sufixo _VALUE para cada valor de enumeração.

O idioma .proto permite que vários símbolos de enumeração tenham o mesmo valor numérico. Símbolos com o mesmo valor numérico são sinônimos. Por exemplo:

enum Foo {
  BAR = 0;
  BAZ = 0;
}

Nesse caso, BAZ é sinônimo de BAR. Em Java, BAZ será definido como um campo final estático, como este:

static final Foo BAZ = BAR;

Assim, BAR e BAZ são comparados iguais, e BAZ nunca deve aparecer em instruções de alternância. O compilador sempre escolhe o primeiro símbolo definido com um determinado valor numérico como a versão "canônica" desse símbolo. Todos os símbolos subsequentes com o mesmo número são apenas aliases.

Uma enumeração pode ser definida aninhada em um tipo de mensagem. O compilador gera a definição de enumeração Java aninhada na classe desse tipo de mensagem.

Extensões (somente proto2)

Você recebeu uma mensagem com um intervalo de extensões:

message Foo {
  extensions 100 to 199;
}

O compilador de buffer de protocolo fará com que Foo estenda GeneratedMessage.ExtendableMessage em vez do GeneratedMessage normal. Da mesma forma, o builder da Foo vai estender o GeneratedMessage.ExtendableBuilder. Nunca se refere a esses tipos de base pelo nome (GeneratedMessage é considerado um detalhe de implementação). No entanto, essas superclasses definem vários métodos adicionais que podem ser usados para manipular extensões.

Em especial, Foo e Foo.Builder vão herdar os métodos hasExtension(), getExtension() e getExtensionCount(). Além disso, Foo.Builder herdará os métodos setExtension() e clearExtension(). Cada um desses métodos usa, como seu primeiro parâmetro, um identificador de extensão (descrito abaixo), que identifica um campo de extensão. Os parâmetros restantes e o valor de retorno são exatamente os mesmos dos métodos do acessador correspondentes que seriam gerados para um campo normal (sem extensão) do mesmo tipo do identificador da extensão.

Com uma definição de extensão:

extend Foo {
  optional int32 bar = 123;
}

O compilador de buffer de protocolo gera um "identificador de extensão" chamado bar, que você pode usar com os acessadores de extensão de Foo para acessar esta extensão, desta forma:

Foo foo =
  Foo.newBuilder()
     .setExtension(bar, 1)
     .build();
assert foo.hasExtension(bar);
assert foo.getExtension(bar) == 1;

A implementação exata dos identificadores de extensão é complicada e envolve o uso mágico de genéricos. No entanto, você não precisa se preocupar com o modo como os identificadores de extensão funcionam para usá-los.

Observe que bar seria declarado como um campo estático da classe de wrapper do arquivo .proto, conforme descrito acima. O nome da classe de wrapper foi omitido no exemplo.

As extensões podem ser declaradas dentro do escopo de outro tipo para prefixar os nomes de símbolos gerados. Por exemplo, um padrão comum é estender uma mensagem por um campo dentro da declaração do tipo do campo:

message Baz {
  extend Foo {
    optional Baz foo_ext = 124;
  }
}

Nesse caso, uma extensão com identificador foo_ext e tipo Baz é declarada na declaração de Baz. Para se referir a foo_ext, é necessário adicionar o prefixo Baz.:

Baz baz = createMyBaz();
Foo foo =
  Foo.newBuilder()
     .setExtension(Baz.fooExt, baz)
     .build();
assert foo.hasExtension(Baz.fooExt);
assert foo.getExtension(Baz.fooExt) == baz;

Ao analisar uma mensagem que pode ter extensões, você precisa fornecer um ExtensionRegistry no qual já registrou as extensões que quer analisar. Caso contrário, essas extensões serão tratadas como campos desconhecidos, e os métodos que observam as extensões se comportarão como se não existissem.

  ExtensionRegistry registry = ExtensionRegistry.newInstance();
  registry.add(Baz.fooExt);
  Foo foo = Foo.parseFrom(input, registry);
  assert foo.hasExtension(Baz.fooExt);
  ExtensionRegistry registry = ExtensionRegistry.newInstance();
  Foo foo = Foo.parseFrom(input, registry);
  assert foo.hasExtension(Baz.fooExt) == false;

Serviços

Se o arquivo .proto contiver a seguinte linha:

option java_generic_services = true;

Em seguida, o compilador de buffer de protocolo gerará o código com base nas definições de serviço encontradas no arquivo, conforme descrito nesta seção. No entanto, o código gerado pode ser indesejável, porque não está vinculado a nenhum sistema RPC específico e, portanto, exige mais níveis de indireção do que o código personalizado para um sistema. Se você NÃO quiser que esse código seja gerado, adicione esta linha ao arquivo:

option java_generic_services = false;

Se nenhuma das linhas acima for fornecida, a opção padrão será false, já que o uso de serviços genéricos foi descontinuado. Observe que, antes de 2.4.0, a opção padrão é true.

Os sistemas de RPC baseados em definições de serviço da linguagem .proto precisam fornecer plug-ins para gerar o código adequado para o sistema. Esses plug-ins provavelmente exigem que os serviços abstratos sejam desativados para que possam gerar as próprias classes com os mesmos nomes. Os plug-ins são novos na versão 2.3.0 (janeiro de 2010).

O restante desta seção descreve o que o compilador de buffer de protocolo gera quando os serviços abstratos são ativados.

Interface

Com uma definição de serviço:

service Foo {
  rpc Bar(FooRequest) returns(FooResponse);
}

O compilador de buffer de protocolo gerará uma classe abstrata Foo para representar esse serviço. O Foo vai ter um método abstrato para cada método definido na definição de serviço. Nesse caso, o método Bar é definido como:

abstract void bar(RpcController controller, FooRequest request,
                  RpcCallback<FooResponse> done);

Os parâmetros são equivalentes aos parâmetros de Service.CallMethod(), exceto que o argumento method está implícito e que request e done especificam o tipo exato deles.

Foo cria uma subclasse da interface Service. O compilador de buffer de protocolo gera automaticamente implementações dos métodos de Service da seguinte maneira:

O seguinte método estático também é gerado:

  • static ServiceDescriptor getServiceDescriptor(): retorna o descritor do tipo, que contém informações sobre quais métodos esse serviço tem e quais são os tipos de entrada e saída.

A Foo também conterá uma interface aninhada Foo.Interface. Essa é uma interface pura que contém novamente os métodos correspondentes a cada método na sua definição de serviço. No entanto, essa interface não estende a Service. Isso é um problema porque as implementações do servidor RPC geralmente são escritas para usar objetos Service abstratos, e não o serviço específico. Para resolver esse problema, se você tiver um objeto impl implementando Foo.Interface, chame Foo.newReflectiveService(impl) para construir uma instância de Foo que simplesmente delega impl e implementa Service.

Em resumo, ao implementar seu próprio serviço, você tem duas opções:

  • Subclassifique Foo e implemente os métodos dela conforme apropriado. Em seguida, coloque as instâncias da subclasse diretamente na implementação do servidor RPC. Isso geralmente é mais fácil, mas alguns consideram isso menos "puro".
  • Implemente Foo.Interface e use Foo.newReflectiveService(Foo.Interface) para construir um Service unindo-o. Em seguida, transmita o wrapper para sua implementação de RPC.

Stub

O compilador de buffer de protocolo também gera uma implementação de stub de cada interface de serviço, que é usada por clientes que querem enviar solicitações para servidores que implementam o serviço. Para o serviço Foo (acima), a implementação de stub Foo.Stub será definida como uma classe aninhada.

A Foo.Stub é uma subclasse da Foo que também implementa os métodos abaixo:

  • Foo.Stub(RpcChannel channel): cria um novo stub que envia solicitações sobre o canal.
  • RpcChannel getChannel(): retorna o canal do stub, conforme transmitido ao construtor.

O stub também implementa cada um dos métodos do serviço como um wrapper em torno do canal. Chamar um dos métodos simplesmente chama channel.callMethod().

A biblioteca de buffers de protocolo não inclui uma implementação de RPC. No entanto, ele inclui todas as ferramentas necessárias para conectar uma classe de serviço gerada a qualquer implementação de RPC arbitrária. Você só precisa fornecer implementações de RpcChannel e RpcController.

Como bloquear interfaces

As classes RPC descritas acima têm semânticas sem bloqueio: ao chamar um método, você fornece um objeto de callback que será invocado quando o método for concluído. Muitas vezes, é mais fácil (embora possivelmente menos escalonável) escrever código usando semântica de bloqueio, em que o método simplesmente não retorna até que seja concluído. Para acomodar isso, o compilador de buffer de protocolo também gera versões de bloqueio da classe de serviço. Foo.BlockingInterface é equivalente a Foo.Interface, mas cada método simplesmente retorna o resultado em vez de chamar um callback. Por exemplo, bar é definido como:

abstract FooResponse bar(RpcController controller, FooRequest request)
                         throws ServiceException;

Semelhante a serviços sem bloqueio, Foo.newReflectiveBlockingService(Foo.BlockingInterface) retorna um BlockingService encapsulando alguns Foo.BlockingInterface. Por fim, Foo.BlockingStub retorna uma implementação de stub de Foo.BlockingInterface que envia solicitações para um BlockingRpcChannel específico.

Pontos de inserção do plug-in

Os plug-ins de gerador de código, que querem estender a saída do gerador de código Java, podem inserir o código dos tipos abaixo usando os nomes dos pontos de inserção fornecidos.

  • outer_class_scope: declarações de membro que pertencem à classe de wrapper do arquivo.
  • class_scope:TYPENAME: declarações de membro que pertencem a uma classe de mensagem. TYPENAME é o nome completo do proto, como package.MessageType.
  • builder_scope:TYPENAME: declarações de membro que pertencem à classe builder da mensagem. TYPENAME é o nome completo do proto, como package.MessageType.
  • enum_scope:TYPENAME: declarações de membro que pertencem a uma classe de enumeração. TYPENAME é o nome do tipo de proto completo, como package.EnumType.
  • message_implements:TYPENAME: declarações de implementação de classe para uma classe de mensagem. TYPENAME é o nome completo do proto, como package.MessageType.
  • builder_implements:TYPENAME: declarações de implementação de classe para uma classe builder. TYPENAME é o nome completo do proto, como package.MessageType.

O código gerado não pode conter instruções de importação, porque elas são propensas a entrar em conflito com nomes de tipos definidos no próprio código. Em vez disso, ao se referir a uma classe externa, você precisa usar sempre o nome totalmente qualificado dela.

A lógica para determinar os nomes dos arquivos de saída no gerador de código Java é bastante complicada. Você provavelmente deve analisar o código-fonte protoc, especialmente java_headers.cc, para garantir que tenha abordado todos os casos.

Não gere código que dependa de membros de classe privada declarados pelo gerador de código padrão, porque esses detalhes de implementação podem mudar em versões futuras dos buffers de protocolo.

Aulas de utilitários

O buffer de protocolo fornece classes de utilitário para comparação de mensagens, conversão JSON e para trabalhar com tipos conhecidos (mensagens de buffer de protocolo predefinidas para casos de uso comuns).