Dmytro Rostopira
Dmytro Rostopira

Reputation: 11185

Correct way to use reified generic in Kotlin

I want to rewrite little extension function for SharedPreferences. Right now it looks like this

fun SharedPreferences.put(arg: Pair<String, Any>) {
    val e = edit()
    val s = arg.second
    when (s) {
        is Int -> e.putInt(arg.first, s)
        is String -> e.putString(arg.first, s)
        is Boolean -> e.putBoolean(arg.first, s)
        is Long -> e.putLong(arg.first, s)
        else -> throw NotImplementedError("Extension not implemented for this type")
    }
    e.apply()
}

Is there are any idiomatic way to use generic with when?

My first guess is

inline fun <reified T> SharedPreferences.put(arg: Pair<String, T>) {
    val e = edit()
    when (T::class) {
        Int::class -> e.putInt(arg.first, arg.second as Int)
        String::class -> e.putString(arg.first, arg.second as String)
        Boolean::class -> e.putBoolean(arg.first, arg.second as Boolean)
        Long::class -> e.putLong(arg.first, arg.second as Long)
        else -> throw NotImplementedError("Extension not implemented for this type")
    }
    e.apply()
}

But it's looking a bit creepy and not working

Upvotes: 0

Views: 837

Answers (3)

Sanush
Sanush

Reputation: 325

This the way to implement generic shared preference functions. Use these two functions I mentioned in bellowed in your util class (whatever class that isolated from your activity or fragment)

    inline fun <reified T> SharedPreferences.get(key: String, defaultValue: T): T {
    when (T::class) {
        Boolean::class -> return this.getBoolean(key, defaultValue as Boolean) as T
        Float::class -> return this.getFloat(key, defaultValue as Float) as T
        Int::class -> return this.getInt(key, defaultValue as Int) as T
        Long::class -> return this.getLong(key, defaultValue as Long) as T
        String::class -> return this.getString(key, defaultValue as String) as T
        else -> {
            if (defaultValue is Set<*>) {
                return this.getStringSet(key, defaultValue as Set<String>) as T
            }
        }
    }

    return defaultValue
}

inline fun <reified T> SharedPreferences.put(key: String, value: T) {
    val editor = this.edit()

    when (T::class) {
        Boolean::class -> editor.putBoolean(key, value as Boolean)
        Float::class -> editor.putFloat(key, value as Float)
        Int::class -> editor.putInt(key, value as Int)
        Long::class -> editor.putLong(key, value as Long)
        String::class -> editor.putString(key, value as String)
        else -> {
            if (value is Set<*>) {
                editor.putStringSet(key, value as Set<String>)
            }
        }
    }

    editor.apply()
}

Now you can use these two functions in your activity/fragment

Activity

val sharedPreferences = getSharedPreferences(prefKey, Context.MODE_PRIVATE) 
sharedPreferences.put(key, valueYouWantoStore) //value can be any type mentioned in put function
val value = sharedPreferences.get(Enums.signInState, defaultvalue)

Fragment

val sharedPreferences = this.requireActivity().getSharedPreferences(prefKey, Context.MODE_PRIVATE)
sharedPreferences.put(key, valueYouWantoStore) //value can be any type mentioned in put function
val value = sharedPreferences.get(Enums.signInState, defaultvalue)

Upvotes: 0

marstran
marstran

Reputation: 28066

If you want to avoid the when-statement, you can create overloads for each type:

inline fun SharedPreferences.put(arg: Pair<String, Int>) {
    edit().putInt(arg.first, arg.second).apply()
}

inline fun SharedPreferences.put(arg: Pair<String, String>) {
    edit().putString(arg.first, arg.second).apply()
}

inline fun SharedPreferences.put(arg: Pair<String, Boolean>) {
    edit().putBoolean(arg.first, arg.second).apply()
}

inline fun SharedPreferences.put(arg: Pair<String, Long>) {
    edit().putLong(arg.first, arg.second).apply()
}

Upvotes: 2

glee8e
glee8e

Reputation: 6429

Your first version is the correct and idiomatic way. Kotlin has smart cast, so you have is Int -> e.putInt(arg.first, s) instead of is Int -> e.putInt(arg.first, s as Int). That is all kotlin can do for you.

Upvotes: 2

Related Questions