https://kotlinlang.org logo
#getting-started
Title
# getting-started
d

Daniele B

03/21/2021, 5:35 PM
how can I make an instance of the reified class type?
Copy code
inline fun <reified T:Any> getResponse(endpoint : String): T? {
	
	val newObj = T()
	....

    }
y

Youssef Shoaib [MOD]

03/21/2021, 5:43 PM
Well the issue is like there's no guarantee that that reified class will have a zero-argument constructor or that it'll even have a constructor in the first place (it could be an object) and so this really isn't possible and doesn't make sense. Your best bet is to also take in a function that takes no arguments and gives you an instance of T like this:
Copy code
inline fun <reified T:Any> getResponse(endpoint : String, creator: () -> T): T? {
val newObj = creator()
....
}

// Call site
getResponse(endpoint, ::Test) //uses Test's no argument constructor
getResponse(endpoint) { new MyClass(arg1, arg2, arg3) }
👍 1
d

Daniele B

03/21/2021, 5:53 PM
I am a bit confused :-( I give you a more specific example:
Copy code
class ScreenState<T>(
    val isLoading: Boolean = false,
    val screenData: T? = null,
)

inline fun <reified T:Any> getScreenState(screenState: ScreenState<T>) : ScreenState<T> {
    var screenData = screenState.screenData
    if (screenData == null) {
        val screenData = T()
    }
    return ScreenState<T>(screenState.isLoading, screenData)
}
y

Youssef Shoaib [MOD]

03/21/2021, 5:57 PM
Then for it to work it'll basically be this:
Copy code
class ScreenState<T>(
    val isLoading: Boolean = false,
    val screenData: T? = null,
)
inline fun <reified T:Any> newScreenState(screenState: ScreenState<T>, dataCreator: () -> T) : ScreenState<T> {
    var screenData = screenState.screenData
    if (screenData == null) {
        val screenData = dataCreator()
    }
    return ScreenState<T>(screenState.isLoading, screenData)
}

// Call site
newScreenState(myDetailState) { new DetailData(CountryInfo(), ...) }
d

Daniele B

03/21/2021, 5:59 PM
ok, I see, thanks so the object cannot be created inside the reified function, but it always has to be created outside and passed to it?
I mean, it wouldn’t work in a generic way, right?
you always have to manually specify the class
y

Youssef Shoaib [MOD]

03/21/2021, 6:00 PM
Kinda. Basically there's just no way in Kotlin for you to generically guarantee that a function will have a zero argument constructor or a constructor at all for that matter and so your only option is to pass the function a lambda that creates an object of that class on demand
d

Daniele B

03/21/2021, 6:01 PM
what about if I know that the class has the constructor with all parameters having a default value?
(but I guess it’s not very safe, in case I forget to specify all default values, anyway)
y

Youssef Shoaib [MOD]

03/21/2021, 6:07 PM
Well so you can do that with reflection technically but it is definitely discouraged. But yeah with reflection what you can do is this:
Copy code
inline fun <reified T:Any> newScreenState(screenState: ScreenState<T>) : ScreenState<T> {
    var screenData = screenState.screenData
    if (screenData == null) {
        val screenData = T::class.primaryConstructor!!.callBy()
    }
    return ScreenState<T>(screenState.isLoading, screenData)
}
Basically any KClass instance has a primary constructor that is either a
KFunction
or null, and so you can then attempt to call that
KFunction
with no parameters and let it blow up if it doesn't accept zero arguments. That will account for optional parameters in your use case so this should work just fine
d

Daniele B

03/21/2021, 6:10 PM
I am writing the code in CommonMain for a Multiplatform project. Android Studio doesn’t show me the “primaryConstructor” property. Maybe reflection is just for JVM?
y

Youssef Shoaib [MOD]

03/21/2021, 6:12 PM
You can also make it safer by iterating over all the constructors available for that class like this:
Copy code
inline fun <reified T:Any> newScreenState(screenState: ScreenState<T>) : ScreenState<T> {
    var screenData = screenState.screenData
    if (screenData == null) {
        val screenData = T::class.constructors.first { it.parameters.all { it.isOptional } }.callBy()
    }
    return ScreenState<T>(screenState.isLoading, screenData)
}
Oh yes right I forgot a lot of the reflection functionality is JVM-only so yeah in your case you have to resort to the lambda route
d

Daniele B

03/21/2021, 6:13 PM
Ok, thanks so much for your support!
y

Youssef Shoaib [MOD]

03/21/2021, 6:14 PM
Absolutely no problem whatsoever. Happy to help!
❤️ 1
131 Views