Quiero aprovechar las expresiones when de kotlin y los métodos genéricos para simplificar la API de preferencias compartidas de Android.

En lugar de llamar a getString () & getInt (), etc. todo el tiempo, lo que quiero hacer es crear una función de extensión que cambie según el tipo de función de retorno y llame al método apropiado. Algo como a continuación:

  fun <T> SharedPreferences.get(key: String): T? {
        when (T) { //how do I switch on return type and call appropriate function?
            is String -> getString(key, null)
            is Int -> getInt(key, -1)
            is Boolean -> getBoolean(key, false)
            is Float -> getFloat(key, -1f)
            is Long -> getLong(key, -1)
        }
        return null
    }

Por supuesto, no funcionará. Pero, ¿hay alguna solución para usar cuando la expresión para el tipo de retorno de una función? Todas las sugerencias son bienvenidas.

11
Krupal Shah 18 dic. 2016 a las 02:00

1 respuesta

La mejor respuesta

Para lograr exactamente lo que desea, puede utilizar parámetros de tipo reificado . Esto hará que el compilador inserte su función en sus sitios de llamada con T reemplazado por el tipo utilizado en el sitio de llamada.

La función se vería así:

@Suppress("IMPLICIT_CAST_TO_ANY")
inline operator fun <reified T> SharedPreferences.get(key: String): T? =
    when (T::class) {
        String::class -> getString(key, null)
        Int::class -> getInt(key, -1)
        Boolean::class -> getBoolean(key, false)
        Float::class -> getFloat(key, -1f)
        Long::class -> getLong(key, -1)
        else -> null
    } as T?

Si crea get una operator función , también puede llamarlo usando la sintaxis del operador: prefs[name].

Las llamadas deben, por supuesto, proporcionar suficiente información de tipo para que el compilador infiera T:

val i: Int? = prefs["i"] // OK, the type information is taken from the declaration
val j: Int = prefs["i"]!! // OK

val x = prefs["x"] // Error, not enough type information
val y = prefs.get<String>("y") // OK, the type will be `String?`

fun f(z: Int) = z
f(prefs["z"]!!) // OK, the type information is taken from the parameter type
11
hotkey 20 dic. 2016 a las 13:17
1
@KrupalShah, en Kotlin, las inline funciones de hecho no están previstas para ser una herramienta de rendimiento porque la propia JVM administra funciones en línea en tiempo de ejecución lo suficientemente bien. En cambio, las funciones inline se utilizan para mayor flexibilidad. Los dos casos de uso principales son parámetros de tipo reificado y flujo de control no local (break y continue podrían agregarse a return en el futuro).
 – 
hotkey
18 dic. 2016 a las 13:34
1
@KrupalShah, tenga en cuenta que ambos casos de uso requieren que se transforme el cuerpo de la función (para los genéricos reificados, el parámetro de tipo se reemplaza con el tipo presente en el sitio de la llamada, porque de lo contrario el parámetro de tipo sería borrado, y el código no pudo usarlo; para el flujo de control, el argumento funcional está en línea, por lo que return dentro funcionaría con la función exterior). Estas transformaciones se realizan para cada una de las llamadas a funciones y, a continuación, el código transformado se inserta en los sitios de llamada.
 – 
hotkey
18 dic. 2016 a las 13:39
1
Buena solución, pero podría ser mejor solo obtener la clase de T en la función en línea y extraer el resto del cuerpo a otra función no en línea para minimizar la cantidad de código en línea en el sitio de llamada.
 – 
Ilya
21 dic. 2016 a las 02:45