Jak działa słowo kluczowe reified w Kotlinie?


144

Próbuję zrozumieć cel reifiedsłowa kluczowego, najwyraźniej pozwala nam to na refleksję na temat leków generycznych .

Jednak kiedy to pominę, działa równie dobrze. Czy ktoś chciałby wyjaśnić, kiedy to rzeczywiście robi różnicę ?


Czy na pewno wiesz, czym jest odbicie? Czy słyszałeś o usuwaniu typów?
— Mibac

3
Ogólne parametry typu są usuwane w czasie wykonywania, przeczytaj o usuwaniu typów, jeśli jeszcze tego nie zrobiłeś. Reifikowane parametry typu w funkcjach wbudowanych nie tylko wstawiają treść metody, ale także parametr typu ogólnego, umożliwiając wykonywanie takich czynności jak T :: class.java (czego nie można zrobić w przypadku normalnych typów ogólnych). Dodam jako komentarz, ponieważ nie mam teraz czasu na sformułowanie pełnej odpowiedzi ..
— F. George,

Pozwala uzyskać dostęp do konkretnego typu generycznego funkcji bez polegania na refleksji i bez konieczności przekazywania typu jako argumentu.
— BladeCoder

Odpowiedzi:


368

TL; DR: Co jest reifieddobre dla

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

W treści funkcji ogólnej, takiej jak myGenericFun, nie można uzyskać dostępu do typu, Tponieważ jest on dostępny tylko w czasie kompilacji, ale jest usuwany w czasie wykonywania. Dlatego jeśli chcesz użyć typu ogólnego jako normalnej klasy w treści funkcji, musisz jawnie przekazać klasę jako parametr, jak pokazano w myGenericFun.

Jeśli utworzysz inlinefunkcję z reified T , typ Tmożna uzyskać nawet w czasie wykonywania, a zatem nie musisz Class<T>dodatkowo przekazywać . Można pracować z Ttak jakby to było normalne klasy, na przykład może chcesz sprawdzić, czy zmienna jest instancją T , które można łatwo zrobić wtedy: myVar is T.

Taka inlinefunkcja z reifiedtypem Twygląda następująco:

inline fun <reified T> myGenericFun()

Jak reifieddziała

Możesz używać tylko reifiedw połączeniu z inlinefunkcją . Taka funkcja powoduje, że kompilator kopiuje kod bajtowy funkcji w każde miejsce, w którym funkcja jest używana (funkcja jest „wstawiana”). Kiedy wywołujesz funkcję wbudowaną z typem reified, kompilator zna rzeczywisty typ używany jako argument typu i modyfikuje wygenerowany kod bajtowy, aby bezpośrednio używał odpowiedniej klasy. Dlatego wywołania takie jak myVar is Tget myVar is String(jeśli argument typu był String) w kodzie bajtowym iw czasie wykonywania.


Przykład

Spójrzmy na przykład, który pokazuje, jak pomocne reifiedmoże być. Chcemy utworzyć funkcję rozszerzającą dla Stringwywoływanej, toKotlinObjectktóra próbuje przekonwertować ciąg JSON na zwykły obiekt Kotlin o typie określonym przez typ ogólny funkcji T. Możemy com.fasterxml.jackson.module.kotlindo tego użyć, a pierwsze podejście jest następujące:

a) Pierwsze podejście bez typu zreifikowanego

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

readValueMetoda zajmuje typ, który to ma do analizowania JsonObjectdo. Jeśli spróbujemy pobrać Classparametr typu T, kompilator narzeka: „Nie można użyć 'T' jako parametru typu reified. Zamiast tego użyj klasy.”

b) Obejście z jawnym Classparametrem

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

Aby obejść ten problem, Classof Tmożna ustawić jako parametr metody, który następnie zostanie użyty jako argument funkcji readValue. To działa i jest częstym wzorcem w ogólnym kodzie Java. Można to nazwać w następujący sposób:

data class MyJsonType(val name: String)

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

c) Droga Kotlina: reified

Użycie inlinefunkcji z reifiedparametrem typu Tumożliwia inną implementację funkcji:

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

Nie ma potrzeby brać dodatkowego Classudziału T, Tmożna z niej korzystać tak, jakby to była zwykła klasa. Dla klienta kod wygląda następująco:

json.toKotlinObject<MyJsonType>()

Ważna uwaga: praca z Javą

Funkcja wstawiana z reifiedtypem nie jest wywoływana z kodu Java .


6
Dziękuję za wyczerpującą odpowiedź! To naprawdę ma sens. Zastanawiam się tylko nad jedną rzeczą, dlaczego jest potrzebna reifikacja, jeśli funkcja i tak jest wstawiana? Czy mimo to pozostawiłoby to wymazanie typu i wbudowane w funkcję? Wydaje mi się, że to trochę marnotrawstwo, jeśli wstawisz funkcję, możesz również umieścić w niej używany typ, czy też widzę tutaj coś nie tak?
— hl3mukkel

6
Dziękuję za twoją opinię, właściwie zapomniałem wspomnieć o czymś, co może dać ci odpowiedź: normalną funkcję inline można wywołać z Javy, ale taką z parametrem typu reified nie! Myślę, że jest to powód, dla którego nie każdy parametr typu funkcji wbudowanej jest automatycznie reifikowany.
— s1m0nw1

A co, jeśli funkcja jest połączeniem parametrów zreifikowanych i niezreifikowanych? To sprawia, że ​​i tak nie można go wywoływać z języka Java, dlaczego nie ponownie określić wszystkich parametrów typu automatycznie? Dlaczego kotlin musi jawnie reifikować wszystkie parametry typu?
— Vairavan,

1
co się stanie, jeśli wyższe wywołujące w stosie nie potrzebują json.toKotlinObject <MyJsonType> (), ale json.toKotlinObject <T> () dla różnych obiektów?
— siódmy

1

PROSTY

* reified to udzielenie pozwolenia na użycie w czasie kompilacji (aby uzyskać dostęp do funkcji T inside de)

na przykład:

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

    val gson = Gson()

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

}

używając takich jak:

val jsonStringResponse = "{"name":"bruno" , "age":"14" , "world":"mars"}"
val userObject = jsonStringResponse.convertToObject<User>()
  println(user.name)
Korzystając z naszej strony potwierdzasz, że przeczytałeś(-aś) i rozumiesz nasze zasady używania plików cookie i zasady ochrony prywatności.
Licensed under cc by-sa 3.0 with attribution required.