Generic class type usage in Kotlin

2019-04-29 09:29发布

问题:

I am trying to write a generic base activity, that specifies it's ViewModel type a generic parameter:

abstract class BaseActivity<T : ViewModel> : AppCompatActivity()

Now I am trying to write a lazy initialized property for my ViewModel:

val viewModel by lazy { ViewModelProviders.of(this, getFactory()).get(T) }

The error that is displayed is Type Parameter T is not an expression

Also using ::class or ::class.java did not help. Can anyone explain the problem?

EDIT: I tried to use a reified inline function like this:

inline fun <reified T : ViewModel?> AppCompatActivity.obtainViewModel(factory: ViewModelProvider.Factory): T {
    return ViewModelProviders.of(this, factory).get(T::class.java)
}

And used it like this:

abstract class BaseActivity<T> : AppCompatActivity() {
    val viewModel by lazy { obtainViewModel<T>(getFactory()) 
}

Now I get the error, that T cannot be used as a reified parameter.

EDIT2: The best solution so far seems to be this: Link, but its overhead to implement the abstract token in every extending class.

回答1:

Your class has a type parameter T, which unfortunately gets erased at runtime. As a result, the call get(T) does not work (I guess the method expects a Class actually?).

You seem to have already noticed that and thus tried to fix it with encapsulating the handling into a method using reified type. Yet, you cannot pass T as a reified parameter since this type will already be erased when the reified-typed method is called. As a result, obtainViewModel<T> will not work either. What you can do, is using T for ordinary generic methods, which the following demonstrates:

class Typed<T> {
    val lazyProp by lazy {
        listOf<T>()
    }
}


回答2:

maybe a bit late, but I guess this could be the elegant way to achieve it:

abstract class BaseActivity<T : ViewModel>(
    private var modelClass: Class<T>) : AppCompatActivity() {

    val viewModel by lazy { 
        ViewModelProviders.of(this, getFactory()).get(modelClass) 
    }

}

and then

class SampleActivity : BaseActivity<SampleViewModel>(SampleViewModel::class.java) {
    // use viewModel from here
}