Search code examples
javaandroidkotlinandroid-typeface

android return non-null typeface by kotlin


I am practicing to convert java code to Kotlin code:

class TypefaceProvider {
    companion object {
        private var tmpTypeface = Hashtable<String,Typeface>()
        @JvmStatic
        fun getTypeface(context : Context, dir : String, name : String) : Typeface {
            var createTypeface : Typeface? = tmpTypeface[name]
            if(createTypeface == null){
                createTypeface = try{
                    Typeface.createFromAsset(context.assets,"$dir/$name")
                } catch (e : Exception){
                    Log.e(TAG, "fail. msg:${e.message}")
                    Typeface.DEFAULT
                } finally {
                    tmpTypeface[name] = createTypeface 
                }
            }
            return createTypeface <--- wrong, type mismatch
        }
    }
}

I want to return an non-null typeface object,

I have already checked createTypeface is not null.

How to fix the return type mismatch problem?

Should I use return createTypeface!! or

createTypeface?:Typeface.DEFAULT (but Typeface.DEFAULT is duplicate)

or otherwise?


Solution

  • There short answer is -- If you are making sure createTypeface is truly not null, returning createTypeface!! is safe to do. You are asserting something you know to be true. It appears you are doing that, so !! should work fine in this case.

    In general, if you want Kotlin to infer that something is not null, you need to make sure you never have null as possible value for it. One option for your example is to collapse your null check into a ?: operator --

    class TypefaceProvider {
        companion object {
            private var tmpTypeface = Hashtable<String, Typeface>()
            @JvmStatic
            fun getTypeface(context: Context, dir: String, name: String): Typeface {
                val createTypeface = tmpTypeface[name] ?: try {
                    Typeface.createFromAsset(context.assets, "$dir/$name")
                } catch (e: Exception) {
                    Log.e(TAG, "fail. msg:${e.message}")
                    Typeface.DEFAULT
                }
                tmpTypeface[name] = createTypeface`
                return createTypeface
            }
        }
    }
    

    Note that we had to pull the tmpTypeface[name] = createTypeface out of the finally block. It causes your cache to always save a null value (see @Alexy Romanov's comment below). As an aside, I feel it is better to not have it in the finally block anyway, a side-effect inside a variable assignment seems odd. Also, we don't have to give createTypeface and explicit type since it can be inferred.

    If you want something more readable, you can break the loading/caching logic into a separate method --

    class TypefaceProvider {
        companion object {
            private var tmpTypeface = Hashtable<String, Typeface>()
    
            private fun loadTypeFace(context: Context, dir: String, name: String): Typeface {
                val typeface = try {
                    Typeface.createFromAsset(context.assets, "$dir/$name")
                } catch (e: Exception) {
                    Typeface.DEFAULT
                }
                tmpTypeface[name] = typeface
                return typeface
            }
    
            @JvmStatic
            fun getTypeface(context: Context, dir: String, name: String) = 
                tmpTypeface[name] ?: loadTypeFace(context, dir, name)
        }
    }