Search code examples
javakotlinpreferencesdelegated-properties

Java Preferences API with Kotlin delegated properties


I want to use the Java Preferences API to store some data for my application. Since I'm writing the application in Kotlin, I would like to take advantage of delegated properties to get and set my preferences.

My current setup is like this

object Pref {
    val systemPref by lazy { Preferences.systemNodeForPackage(App::class.java) }
    val userPref by lazy { Preferences.userNodeForPackage(App::class.java) }

    // example preference
    const val SETTING = "setting"
    var setting: Int
        get() = userPref.getInt(SETTING, -1)
        set(value) = userPref.putInt(SETTING, value)
}

But I would like to have something like this to declare the preferences

var setting: Int by preference(userPref, "setting", -1)

The API has different methods for accessing preferences of different data types. I would like to use those, but it complicates things for me. Is there a way to have such a delegate which would work with any data type and also wouldn't convert everything to and from strings when talking to the library?


Solution

  • You could make the delegate's constructor take a KClass parameter for which you inspect the type to get the right type of preference back. Then write your preference function with a reified type so it can instantiate the delegate appropriately. Something like this should be usable with the syntax you demonstrated:

    inline fun <reified T: Any> preference(preferences: Preferences, key: String, defaultValue: T)
            = PreferenceDelegate(preferences, key, defaultValue, T::class)
    
    class PreferenceDelegate<T: Any>(
        val preferences: Preferences,
        val key: String,
        val defaultValue: T,
        val type: KClass<T>
    ): ReadWriteProperty<Any, T> {
    
        @Suppress("UNCHECKED_CAST")
        override fun setValue(thisRef: Any, property: KProperty<*>, value: T) {
            with (preferences) {
                when (type) {
                    Int::class -> putInt(key, value as Int)
                    Long::class -> putLong(key, value as Long)
                    Float::class -> putFloat(key, value as Float)
                    Boolean::class -> putBoolean(key, value as Boolean)
                    String::class -> put(key, value as String)
                    ByteArray::class -> putByteArray(key, value as ByteArray)
                    else -> error("Unsupported preference type $type.")
                }
            }
        }
    
        @Suppress("UNCHECKED_CAST")
        override fun getValue(thisRef: Any, property: KProperty<*>): T {
            return with (preferences) {
                when (type) {
                    Int::class -> getInt(key, defaultValue as Int)
                    Long::class -> getLong(key, defaultValue as Long)
                    Float::class -> getFloat(key, defaultValue as Float)
                    Boolean::class -> getBoolean(key, defaultValue as Boolean)
                    String::class -> get(key, defaultValue as String)
                    ByteArray::class -> getByteArray(key, defaultValue as ByteArray)
                    else -> error("Unsupported preference type $type.")
                }
            } as T
        }
    
    }