Home > Software engineering >  Type mismatch using generics
Type mismatch using generics

Time:12-28

I have issues understanding generics and i failed to find the answer here.

Here is the issue:

I have an abstract class that is suppose to be the parent class to few viewmodels. Idea is, one view is going to be created based on data coming from different viewmodels. And i want to draw it based on the same method, just using different Types.

Also, I dont want a return type, i want to trigger some callbacks.

Here is the abstract:

package foo.bar.ui.app.user_profile.view_model


abstract class UserDefaultsGenericViewModel : BaseViewModel() {
    abstract fun <P> getData(
        data: (P) -> Unit,
        error: (Result.Error) -> Unit
    )
} 

And then example of one ViewModel is like this:

package foo.bar.ui.app.user_profile.view_model


@HiltViewModel
class StopViewModel @Inject constructor(
    private val getStopsByRouteUseCase: ParamsUseCase<RouteParams, Stops>
) : UserDefaultsGenericViewModel() {

    var stopId = ""
    override fun <Stops> getData(data: (Stops) -> Unit, error: (Result.Error) -> Unit) {
        viewModelScope.launch {
            when (val resultStops = getStopsByRouteUseCase.invoke(RouteParams(stopId, Direction.ToWork))) {
                is Result.Success -> {
                    data.invoke(resultStops.value)
                }
                is Result.Error -> Log.e("TAG", "bar")
            }
        }
    }
}

The problem is in this line: data.invoke(resultStops.value)

Im getting:

Type mismatch: inferred type is foo.bar.onboarding.Stops but Stops#1 (type parameter of foo.bar.ui.app.user_profile.view_model.StopViewModel.getData) was expected

What am i doing wrong?

CodePudding user response:

You're using a generic method, but it looks like you want a generic class/interface.

In your override fun <Stops> getData, Stops is an arbitrary name of a type parameter, not the actual Stops type that you seem to want. What you probably want instead is the following:

// note the <P> at the class level
abstract class UserDefaultsGenericViewModel<P> : BaseViewModel() {

    // no <P> here after the fun keyword
    abstract fun getData(
        data: (P) -> Unit,
        error: (Result.Error) -> Unit
    )
}

@HiltViewModel
class StopViewModel @Inject constructor(
    private val getStopsByRouteUseCase: ParamsUseCase<RouteParams, Stops>
) : UserDefaultsGenericViewModel<Stops>() { // note the <Stops> type argument here

    ...
}

Here the <P> is on the class declaration, so that <P> is determined once for each instance of the class. If you declare the generic on the method, the actual type can be different for each method invocation.

  • Related