Как использовать TypeToken + generics с Gson в Kotlin

Я не могу получить список универсального типа из пользовательского класса (Turns):

val turnsType = TypeToken<List<Turns>>() {}.type val turns = Gson().fromJson(pref.turns, turnsType) 

он сказал:

 cannot access '<init>' it is 'public /*package*/' in 'TypeToken' 

Создайте это встроенное удовольствие:

 inline fun <reified T> Gson.fromJson(json: String) = this.fromJson<T>(json, object: TypeToken<T>() {}.type) 

и тогда вы можете назвать это следующим образом:

 val turns = Gson().fromJson<Turns>(pref.turns) // or val turns: Turns = Gson().fromJson(pref.turns) 

ПРИМЕЧАНИЕ . Этот подход не был возможен ранее в старых версиях плагинов kotlin, но теперь вы можете его использовать.


Предыдущие альтернативы:

АЛЬТЕРНАТИВА 1:

 val turnsType = object : TypeToken<List<Turns>>() {}.type val turns = Gson().fromJson<List<Turns>>(pref.turns, turnsType) 

Вы должны поместить object : и конкретный тип fromJson<List<Turns>>


АЛЬТЕРНАТИВА 2:

Как @cypressious упомянуть, что может быть достигнуто также следующим образом:

 inline fun <reified T> genericType() = object: TypeToken<T>() {}.type 

использовать как:

 val turnsType = genericType<List<Turns>>() 

Это решает проблему:

 val turnsType = object : TypeToken<List<Turns>>() {}.type val turns = Gson().fromJson<List<Turns>>(pref.turns, turnsType) 

Первая строка создает выражение объекта, которое происходит от TypeToken а затем получает от него Type Java. Тогда метод Gson().fromJson либо нуждается в типе, указанном для результата функции (который должен соответствовать созданному TypeToken ). Две версии этой работы, как указано выше, или:

 val turns: List<Turns> = Gson().fromJson(pref.turns, turnsType) 

Чтобы упростить создание TypeToken вы можете создать вспомогательную функцию, которая должна быть встроенной, чтобы она могла использовать параметры типа reified :

 inline fun <reified T> genericType() = object: TypeToken<T>() {}.type 

Которые затем могут быть использованы одним из следующих способов:

 val turnsType = genericType<List<Turns>>() // or val turnsType: List<Turns> = genericType() 

И весь процесс может быть завернут в функцию расширения для экземпляра Gson :

 inline fun <reified T> Gson.fromJson(json: String) = this.fromJson<T>(json, object: TypeToken<T>() {}.type) 

Чтобы вы могли просто позвонить Gson и не беспокоиться о TypeToken :

 val turns = Gson().fromJson<Turns>(pref.turns) // or val turns: Turns = Gson().fromJson(pref.turns) 

Здесь Kotlin использует вывод типа с одной стороны задания или другой, и повторно генерирует обобщения для встроенной функции, которая проходит через полный тип (без стирания) и использует это для создания TypeToken а также делает вызов Gson

Это работает также и проще

  inline fun <reified T> Gson.fromJson(json: String) : T = this.fromJson<T>(json, T::class.java) 

Другой вариант (не уверенный, что он выглядит более элегантным, чем другие) может вызвать такой вызов:

 turns = Gson().fromJson<Array<Turns>>(allPurchasesString, Array<Turns>::class.java).toMutableList() 

Таким образом, вы используете линейку класса Java java Array вместо «чистого котлина».