Como a palavra-chave reificada no Kotlin funciona?

144

Estou tentando entender o objetivo da reifiedpalavra - chave, aparentemente está nos permitindo refletir sobre os genéricos .

No entanto, quando deixo de fora, funciona tão bem. Alguém quer explicar quando isso faz uma diferença real ?

hl3mukkel
fonte
Tem certeza de que sabe o que é reflexão? Além disso, você ouviu falar sobre apagamento de tipo?
Mibac 29/08/19
3
Os parâmetros de tipo genérico são apagados no tempo de execução, leia sobre apagamento de tipo, se você ainda não o tiver. Os parâmetros de tipo reificado nas funções embutidas não apenas incorporam o corpo do método, mas também o parâmetro de tipo genérico, que permite fazer coisas como T :: class.java (o que você não pode fazer com tipos genéricos normais). Colocando como um comentário, porque eu não tenho tempo a concretizar uma resposta completa agora ..
F. George
Permite obter acesso ao tipo genérico concreto de uma função sem depender da reflexão e sem ter que passar o tipo como argumento.
precisa saber é o seguinte

Respostas:

368

TL; DR: O que é reifiedbom para

fun <T> myGenericFun(c: Class<T>) 

No corpo de uma função genérica como myGenericFun, você não pode acessar o tipo Tporque ele está disponível apenas em tempo de compilação, mas apagado em tempo de execução. Portanto, se você quiser usar o tipo genérico como uma classe normal no corpo da função que você precisa para passar explicitamente a classe como um parâmetro , como mostrado na myGenericFun.

Se você criar uma inlinefunção com um reificada T no entanto, o tipo de Tpode ser acessado mesmo em tempo de execução e, portanto, você não precisa passar o Class<T>adicionalmente. Você pode trabalhar com Tcomo se fosse uma classe normal, por exemplo, você pode querer verificar se uma variável é uma instância T , que você pode facilmente fazer em seguida: myVar is T.

Essa inlinefunção com o reifiedtipo Ttem a seguinte aparência:

inline fun <reified T> myGenericFun()

Como reifiedfunciona

Você só pode usar reifiedem combinação com uma inlinefunção . Essa função faz com que o compilador copie o bytecode da função para todos os locais em que a função está sendo usada (a função está sendo "embutida"). Quando você chama uma função embutida com o tipo reificado, o compilador conhece o tipo real usado como argumento de tipo e modifica o bytecode gerado para usar diretamente a classe correspondente. Portanto, chamadas como myVar is Ttorne- myVar is Stringse (se o argumento de tipo fosse String) no bytecode e no tempo de execução.


Exemplo

Vamos dar uma olhada em um exemplo que mostra o quão útil reifiedpode ser. Queremos criar uma função de extensão Stringchamada chamada toKotlinObjectque tenta converter uma string JSON em um objeto Kotlin simples com um tipo especificado pelo tipo genérico da função T. Podemos usar com.fasterxml.jackson.module.kotlinisso e a primeira abordagem é a seguinte:

a) Primeira abordagem sem tipo reificado

fun <T> String.toKotlinObject(): T {
      val mapper = jacksonObjectMapper()
                                                        //does not compile!
      return mapper.readValue(this, T::class.java)
}

O readValuemétodo usa um tipo para o qual ele deve analisar o JsonObject. Se tentarmos obter o Classparâmetro type T, o compilador reclama: "Não é possível usar 'T' como parâmetro do tipo reificado. Use uma classe."

b) Solução alternativa com Classparâmetro explícito

fun <T: Any> String.toKotlinObject(c: KClass<T>): T {
    val mapper = jacksonObjectMapper()
    return mapper.readValue(this, c.java)
}

Como solução alternativa, o Classde Tpode ser um parâmetro de método, que é usado como argumento para readValue. Isso funciona e é um padrão comum no código Java genérico. Pode ser chamado da seguinte maneira:

data class MyJsonType(val name: String)

val json = """{"name":"example"}"""
json.toKotlinObject(MyJsonType::class)

c) O caminho Kotlin: reified

O uso de uma inlinefunção com o reifiedparâmetro type Tpermite implementar a função de maneira diferente:

inline fun <reified T: Any> String.toKotlinObject(): T {
    val mapper = jacksonObjectMapper()
    return mapper.readValue(this, T::class.java)
}

Não há necessidade de levar o Classde T, adicionalmente, Tpode ser usado como se fosse uma classe comum. Para o cliente, o código fica assim:

json.toKotlinObject<MyJsonType>()

Nota importante: Trabalhando com Java

Uma função embutida com o reifiedtipo não pode ser chamada do código Java .

s1m0nw1
fonte
6
Obrigado pela sua resposta abrangente! Isso realmente faz sentido. Só uma coisa que eu estou pensando, por que é necessário reificar se a função está sendo incorporada de qualquer maneira? Deixaria o tipo de apagamento e incorporaria a função de qualquer maneira? Isso me parece um desperdício, se você incorporar a função, poderá incorporar também o tipo que está sendo usado ou estou vendo algo errado aqui?
Hl3mukkel 31/08/19
6
Obrigado pelo seu feedback, na verdade eu esqueço de mencionar algo que pode lhe dar a resposta: uma função embutida normal pode ser chamada de Java, mas uma com um parâmetro de tipo reificado não pode! Penso que esta é uma razão pela qual nem todos os parâmetros de tipo de uma função embutida são automaticamente reificados.
S1m0nw1
E se a função for uma mistura de parâmetros reificados e não reificados? De qualquer forma, não é elegível para ser chamado de Java, por que não reificar todos os parâmetros de tipo automaticamente? Por que o kotlin precisa ter reificado especificado para todos os parâmetros de tipo explicitamente?
Vairavan
1
e se os chamadores superiores da pilha não precisarem json.toKotlinObject <MyJsonType> (), mas json.toKotlinObject <T> () para objetos diferentes?
sete
1

SIMPLES

* reified é dar permissão para usar no momento da compilação (para acessar T dentro da função)

por exemplo:

 inline fun <reified T:Any>  String.convertToObject(): T{

    val gson = Gson()

    return gson.fromJson(this,T::class.java)

}

usando como:

val jsonStringResponse = "{"name":"bruno" , "age":"14" , "world":"mars"}"
val userObject = jsonStringResponse.convertToObject<User>()
  println(user.name)
poiu
fonte