I'm reading some code which isn't mine, and I've found an odd construct I can't puzzle out.
The line is:
val myViewModel: MyViewModel = viewModel()
I can guess (roughly) what this is doing, but I'm really unsure about that last bit. viewModle() looks like a call to the parent class, but how would it know to produce the correct type? I'm sure there's some subtly of Kotlin I'm missing here, but I lack enough knowledge to grok it completely.
I found this documentation: <https://developer.android.com/reference/kotlin/androidx/lifecycle/viewmodel/compose/viewModel.composable> but what I'd really like is an explanation of how the function viewModel() binds to the type MyViewModel. I think my Kotlin knowledge just isn't up to the task of puzzling this out.
Also, there are several viewModel() functions on that documentation page, how does the correct viewModel() can get chosen from the several available?
I see this other question that explains what's going on, but it lacks a detailed explantion of the nuts and bolts of Kotlin that makes this work. https://stackoverflow.com/questions/79552487/whats-the-difference-between-viewmodel-myviewmodel-viewmodel-and-viewmo
Most of the code here is pretty obvious, here's a larger snippet. Please ask if you think you need more code. MyViewModel is declared as class MyViewModel : ViewModel() and contains no @Composable functions.
@Composable
fun MyApp() {
val scrollBehavior = TopAppBarDefaults.enterAlwaysScrollBehavior()
Scaffold(
modifier = Modifier.nestedScroll(scrollBehavior.nestedScrollConnection),
topBar = { MyTopAppBar(scrollBehavior = scrollBehavior) }
) {
Surface(
modifier = Modifier.fillMaxSize()
) {
// Right here!!
val myViewModel: MyViewModel = viewModel()
HomeScreen(
myUiState = myViewModel.myUiState,
contentPadding = it
)
}
}
}
viewModel() is a top-level Kotlin function. It lives outside of any Kotlin class. We can pretend that the function signature is:
@Composable public inline fun <reified VM : ViewModel> viewModel()
(there are a bunch of parameters, but they all have default values, so we can safely ignore them for the moment)
The return type is a Kotlin generic. VM : ViewModel includes a constraint, which says "this function will return some subtype of ViewModel, and we can refer to that type in the body of the function as VM".
In your call site, you have:
val myViewModel: MyViewModel = viewModel()
The Kotlin compiler sees that your variable is of type MyViewModel, and so the body of viewModel() will map VM to MyViewModel. If MyViewModel does not extend ViewModel, though, you will get a compiler error, because VM : ViewModel requires that whatever the compiler determines VM is needs to extend from ViewModel.
One of the ways that the function body can use that VM is by the VM::class syntax. That evaluates to KClass, which is a Kotlin object identifying the actual type. If you are used to Java's Class type and places where you would use Foo.class, it's the same basic thing. KClass, coupled with Kotlin's reflection library, lets you do things like invoke constructors. Under the covers, the viewmodel system will eventually call your MyViewModel zero-argument constructor, but that is several layers deep past the viewModel() function. It uses the KClass to accomplish that. The viewmodel system directly does not care whether your viewmodel is a MyViewModel or a BarViewModel, so long as it extends ViewModel.
> Also, there are several viewModel() functions on that documentation page, how does the correct viewModel() can get chosen from the several available?
Only one of them has default values for all the parameters in the actual implementation:
@Composable public inline fun <reified VM : ViewModel> viewModel( viewModelStoreOwner: ViewModelStoreOwner = checkNotNull(LocalViewModelStoreOwner.current) { "No ViewModelStoreOwner was provided via LocalViewModelStoreOwner" }, key: String? = null, factory: ViewModelProvider.Factory? = null, extras: CreationExtras = viewModelStoreOwner.defaultViewModelCreationExtras, ): VM = viewModel(VM::class, viewModelStoreOwner, key, factory, extras)
FWIW, this older free book of mine covers generics.
CommonsWare