How do I use the "reified" keyword meanwhile do not expose other property which should be privite?

eg:

object ModelFactory {

    val modelMap = HashMap<Class<*>, Model>()

    inline fun <reified T : Model> getModel(): T {

        var model = modelMap[T::class.java]

        if (model == null) {
            model = T::class.java.newInstance()
            modelMap[T::class.java] = model
        }

        return model as T
    }
}

as the above code showing,you need to declare public keyword to the modelMap property,otherwise you can’t use this property in the getModel function. But if I code like this,in other place,I can access the modelMap property easily,this is not I expect. So I want to know whether there are some good ways to solve this problem?

// ------------------------------------ divider ----------------------------
According to the @Wasabi375 and @pdvrieze 's answers,I figure out two solutions。

  1. use @PublishedApi
object ModelFactory {

    @PublishedApi
    internal val modelMap = HashMap<Class<*>, Model>()

    inline fun <reified T : Model> getModel(): T {

        var model = modelMap[T::class.java]

        if (model == null) {
            model = T::class.java.newInstance()
            modelMap[T::class.java] = model
        }

        return model as T
    }
}
  1. user overload
object ModelFactory {
    
    private val modelMap = HashMap<Class<*>, Model>()

    inline fun <reified T : Model> getModel(): T {
        return getModel(T::class.java)
    }

    @Suppress("UNCHECKED_CAST")
    fun <T :Model> getModel(clazz: Class<T>):T{
        var model = modelMap[clazz]

        if (model == null) {
            model = clazz.newInstance()
            modelMap[clazz] = model
        }

        return model as T
    }
}

I prefer the second solution,seemingly it can also be used in Java.

modelMap needs to be public because the getModel function is inlined. You can think of it this way: Whenever you call getModel the compiler copies the code instead of calling a function. So

fun foo() {
    val model = getModel<Bar>()
    model.doSomething()
}

gets compiled to

fun foo() {
    // copied code from getModel()
    var _model = modelMap[T::class.java]
    if (_model == null) {
        _model = T::class.java.newInstance()
        modelMap[T::class.java] = _model
    }
    val model = _model as T

    // rest of the foo function
    model.doSomething()
}

That’s why your modelMap property needs to be public. There is however one thing you can do. You can declare it as internal and add the @PublishedAPI annotation to it.

@PublishedAPI 
internal val modelMap

This means kotlin will treat the property as any other internal property but you can still call it from a public inline function.
https://kotlinlang.org/api/latest/jvm/stdlib/kotlin/-published-api/index.html

1 Like

An alternative option is to have two functions. One which takes the reified generic type, and one which just takes a class parameter. You can then have the reified function call the non-reified, non-inline function. This encapsulates the implementation. You could use @PublishedAPI with internal on the non-reified function, but you don’t really need to. It also makes the api accessible from Java.

3 Likes

Thanks a lot!