Esta página descreve exatamente o código Kotlin que o compilador de buffer de protocolo gera para qualquer definição de protocolo, além do código gerado para Java. 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.
Invocação do compilador
O compilador de buffer de protocolo produz código Kotlin que se baseia no código Java. Por isso,
ele precisa ser invocado com duas sinalizações de linha de comando, --java_out=
e --kotlin_out=
.
O parâmetro para a opção --java_out=
é o diretório em que você quer que o compilador grave sua saída Java,
e o mesmo para --kotlin_out=
. 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
.
Independentemente de o arquivo .proto
conter ou não uma linha como a seguinte:
option java_multiple_files = true;
O compilador vai criar arquivos .kt
separados para cada uma das classes e métodos de fábrica que vão ser gerados para cada mensagem de nível superior declarada no arquivo .proto
.
O nome do pacote Java de cada arquivo é o mesmo usado pelo código Java gerado, conforme descrito na referência de código gerado por Java.
O arquivo de saída é escolhido concatenando o parâmetro para --kotlin_out=
, o nome do pacote (com .
s substituídos por /
s) e o nome do arquivo Kt.kt
do sufixo.
Então, por exemplo, digamos que você invoque o compilador da seguinte maneira:
protoc --proto_path=src --java_out=build/gen/java --kotlin_out=build/gen/kotlin src/foo.proto
Se o pacote Java do foo.proto
for com.example
e ele contiver uma mensagem chamada Bar
, o compilador de buffer de protocolo gerará o arquivo build/gen/kotlin/com/example/BarKt.kt
.
O compilador de buffer de protocolo criará automaticamente os diretórios build/gen/kotlin/com
e build/gen/kotlin/com/example
, se necessário. No entanto, ele não criará build/gen/kotlin
, build/gen
ou build
. Eles já devem existir.
É 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.
Mensagens
Se a declaração for simples:
message FooBar {}
O compilador de buffer de protocolo gera, além do código Java gerado, um objeto chamado FooBarKt
, bem como duas funções de nível superior, com a seguinte estrutura:
object FooBarKt { class Dsl private constructor { ... } } inline fun fooBar(block: FooBarKt.Dsl.() -> Unit): FooBar inline fun FooBar.copy(block: FooBarKt.Dsl.() -> Unit): FooBar
Tipos aninhados
Uma mensagem pode ser declarada dentro de outra. Por exemplo:
message Foo {
message Bar {
}
}
Nesse caso, o compilador aninha o objeto BarKt
e o método de fábrica bar
dentro de FooKt
, embora o método copy
permaneça de nível superior:
object FooKt { class Dsl { ... } object BarKt { class Dsl private constructor { ... } } inline fun bar(block: FooKt.BarKt.Dsl.() -> Unit): Foo.Bar } inline fun foo(block: FooKt.Dsl.() -> Unit): Foo inline fun Foo.copy(block: FooKt.Dsl.() -> Unit): Foo inline fun Foo.Bar.copy(block: FooKt.BarKt.Dsl.() -> Unit): Foo.Bar
Campos
Além dos métodos descritos na seção anterior, o compilador de buffer de protocolo gera propriedades mutáveis na DSL para cada campo definido na mensagem no arquivo .proto
.
O Kotlin já infere propriedades somente leitura no objeto de mensagem dos getters gerados pelo Java.
As propriedades sempre usam nomes concatenados, mesmo que o nome do campo no arquivo .proto
use letras minúsculas com sublinhados (como ele deve ser usado). A conversão de caso funciona da seguinte maneira:
- Para cada sublinhado no nome, o sublinhado é removido e a letra a seguir é maiúscula.
- Se o nome tiver um prefixo anexado (por exemplo, "clear"), a primeira letra está em maiúscula. Caso contrário, ela está em letras minúsculas.
Assim, o campo foo_bar_baz
se torna fooBarBaz
.
Em alguns casos especiais, em que um nome de campo entra em conflito com palavras reservadas no Kotlin ou em métodos já definidos na biblioteca protobuf, um sublinhado extra é anexado.
Por exemplo, a liberação de um campo chamado in
é clearIn_()
.
Campos Singular (proto2)
Para qualquer uma dessas definições de campo:
optional int32 foo = 1; required int32 foo = 1;
O compilador gerará os seguintes acessadores na DSL:
fun hasFoo(): Boolean
: retornatrue
se o campo estiver definido.var foo: Int
: o valor atual do campo. Se o campo não for definido, retornará o valor padrão.fun clearFoo()
: limpa o valor do campo. Depois de chamar isso,hasFoo()
retornaráfalse
, egetFoo()
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. Como o tipo de mensagem ainda é definido em Java, os tipos não assinados na mensagem são representados usando os tipos assinados padrão na DSL, para compatibilidade com Java e versões mais antigas do Kotlin.
Campos de mensagens incorporados
Não há processamento especial de submensagens. Por exemplo, se você tiver um campo
optional Foo my_foo = 1;é preciso escrever
myFoo = foo { ... }
Em geral, isso ocorre porque o compilador não sabe se Foo
tem uma DSL do Kotlin
ou, por exemplo, apenas as APIs Java são geradas. Isso significa que você não precisa esperar por
mensagens de que depende para adicionar a geração de código Kotlin.
Também fornecemos um acesso de campo extra FooOrNull
para todos os campos de mensagens opcionais que retornam o valor do campo se ele estiver presente. Caso contrário, ele será nulo.
Campos Singular (proto3)
Para esta definição de campo:
int32 foo = 1;
O compilador gerará a seguinte propriedade na DSL:
var foo: Int
: retorna o valor atual do campo. Se o campo não estiver definido, retorna o valor padrão para o tipo de campo.fun 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. Como o tipo de mensagem ainda é definido em Java, os tipos não assinados na mensagem são representados usando os tipos assinados padrão na DSL, para compatibilidade com Java e versões mais antigas do Kotlin.
Campos de mensagens incorporados
Para tipos de campo de mensagem, um método acessador adicional é gerado na DSL:
boolean hasFoo()
: retornarátrue
se o campo tiver sido definido.
Não há um atalho para definir uma submensagem com base em uma DSL. Por exemplo, se você tiver um campo
Foo my_foo = 1;é preciso escrever
myFoo = foo { ... }
Em geral, isso ocorre porque o compilador não sabe se Foo
tem uma DSL do Kotlin
ou, por exemplo, apenas as APIs Java são geradas. Isso significa que você não precisa esperar por
mensagens de que depende para adicionar a geração de código Kotlin.
Campos repetidos
Para esta definição de campo:
repeated string foo = 1;
O compilador gerará os seguintes membros na DSL:
class FooProxy: DslProxy
, um tipo construtivo usado apenas em genéricosval fooList: DslList<String, FooProxy>
, uma visualização somente leitura da lista de elementos atuais no campo repetidofun DslList<String, FooProxy>.add(value: String)
, uma função de extensão que permite que os elementos sejam adicionados ao campo repetidooperator fun DslList<String, FooProxy>.plusAssign(value: String)
, um alias deadd
fun DslList<String, FooProxy>.addAll(values: Iterable<String>)
, uma função de extensão que permite que umIterable
de elementos seja adicionado ao campo repetidooperator fun DslList<String, FooProxy>.plusAssign(values: Iterable<String>)
, um alias deaddAll
operator fun DslList<String, FooProxy>.set(index: Int, value: String)
, uma função de extensão que define o valor do elemento no índice baseado em zero específico.fun DslList<String, FooProxy>.clear()
, uma função de extensão que limpa o conteúdo do campo repetido
Essa construção incomum permite que fooList
"se comporte como" uma lista mutável dentro do escopo da DSL, oferecendo suporte apenas aos métodos com suporte do builder subjacente,
enquanto evita que a mutabilidade "escape" a DSL, o que pode causar efeitos colaterais confusos.
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.
Oneof Fields
Para esta definição de campo única:
oneof oneof_name { int32 foo = 1; ... }
O compilador gerará os seguintes métodos de acesso na DSL:
val oneofNameCase: OneofNameCase
: recebe quais, se houver, os camposoneof_name
estão definidos. Consulte a referência do código Java para o tipo de retorno.fun hasFoo(): Boolean
(somente proto2): retornatrue
se o um dos casos forFOO
.val foo: Int
: retorna o valor atual deoneof_name
se o caso único forFOO
. Caso contrário, retorna o valor padrão deste 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 do mapa
Para esta definição de campo do mapa:
map<int32, int32> weight = 1;
O compilador gerará os seguintes membros na classe DSL:
class WeightProxy private constructor(): DslProxy()
, um tipo construtivo usado apenas em genéricosval weight: DslMap<Int, Int, WeightProxy>
, uma visualização somente leitura das entradas atuais no campo do mapafun DslMap<Int, Int, WeightProxy>.put(key: Int, value: Int)
: adicione a entrada a esse campo do mapaoperator fun DslMap<Int, Int, WeightProxy>.put(key: Int, value: Int)
: alias deput
usando a sintaxe do operadorfun DslMap<Int, Int, WeightProxy>.remove(key: Int)
: remove a entrada associada akey
, se presente.fun DslMap<Int, Int, WeightProxy>.putAll(map: Map<Int, Int>)
: adiciona todas as entradas do mapa especificado a esse campo de mapa, substituindo os valores anteriores de chaves já presentesfun DslMap<Int, Int, WeightProxy>.clear()
: limpa todas as entradas desse campo do mapa.
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 adicionará os seguintes métodos a FooKt.Dsl
:
operator fun <T> get(extension: ExtensionLite<Foo, T>): T
: recebe o valor atual do campo de extensão na DSL.operator fun <T> get(extension: ExtensionLite<Foo, List<T>>): ExtensionList<T, Foo>
: recebe o valor atual do campo de extensão repetido na DSL como umList
somente leitura.operator fun <T : Comparable<T>> set(extension: ExtensionLite<Foo, T>)
: define o valor atual do campo de extensão na DSL (para tipos de campoComparable
).operator fun <T : MessageLite<T>> set(extension: ExtensionLite<Foo, T>)
: define o valor atual do campo de extensão na DSL (para tipos de campo de mensagem).operator fun set(extension: ExtensionLite<Foo, ByteString>)
: define o valor atual do campo de extensão na DSL (para camposbytes
).operator fun contains(extension: ExtensionLite<Foo, *>): Boolean
: retornará "true" se o campo da extensão tiver um valor.fun clear(extension: ExtensionLite<Foo, *>)
: limpa o campo de extensão.fun <E> ExtensionList<Foo, E>.add(value: E)
: adiciona um valor ao campo de extensão repetida.operator fun <E> ExtensionList<Foo, E>.plusAssign(value: E)
: alias deadd
usando a sintaxe do operadoroperator fun <E> ExtensionList<Foo, E>.addAll(values: Iterable<E>)
: adiciona vários valores ao campo de extensão repetida.operator fun <E> ExtensionList<Foo, E>.plusAssign(values: Iterable<E>)
: alias deaddAll
usando a sintaxe do operadoroperator fun <E> ExtensionList<Foo, E>.set(index: Int, value: E)
: define o elemento do campo de extensão repetido no índice especificadooperator fun ExtensionList<Foo, *>.clear()
: limpa os elementos do campo de extensão repetida.
Os genéricos aqui são complexos, mas o efeito é que this[extension] = value
funciona para todos os tipos de extensão, exceto as extensões repetidas, e as extensões repetidas
têm sintaxe de lista "natural" que funciona de maneira semelhante aos campos repetidos que não são de extensão.
Com uma definição de extensão:
extend Foo { optional int32 bar = 123; }
O Java gera o "identificador de extensão" bar
, que é usado para "principal" operações de extensão acima.