You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Jetpack Compose泛型Lambda参数问题:Preferences.Key泛型报错求解

问题

我尝试在Composable子组件中用onPreferenceChange: (Preferences.Key) -> Unit调用ViewModel的泛型函数,期望编译器能根据值的类型自动推导Preferences.Key的泛型类型,但报错:One type argument expected for class Key<T>。

直接将ViewModel作为SettingsScreenBody的参数不符合开发规范(子组件应向父组件传递事件),还会破坏Composable预览功能。

补充说明:Preferences.Key<>支持的泛型类型包括String、Boolean、Int、Double、Set。

当前代码如下:

@Composable
fun SettingsScreen(
    modifier: Modifier = Modifier,
    viewModel: SettingsScreenViewModel = viewModel(factory = ViewModelProvider.Factory),
) {
    Scaffold { innerPadding ->
        SettingsScreenBody(
            onPreferenceChange = viewModel::setPreferenceValue,
            modifier = Modifier.padding(innerPadding),
        )
    }
}

@Composable
private fun SettingsScreenBody(
    onPreferenceChange: (key: Preferences.Key, value: Any) -> Unit,
    modifier: Modifier = Modifier,
) {
        // App Theme
        ListPreference(
            title = R.string.pref_title_app_theme,
        ) { newValue ->
            onPreferenceChange(PreferenceRepository.Keys.NIGHT_MODE, newValue)
        }
}

class SettingsScreenViewModel(
    private val preferenceRepository: PreferenceRepository,
): ViewModel() {

    val preferences = preferenceRepository.preferences

    fun <T> setPreferenceValue(key: Preferences.Key<T>, value: T) {
        viewModelScope.launch { preferenceRepository.set(key, value) }
    }

}

object Keys {
    val NIGHT_MODE: Preferences.Key<String> = stringPreferencesKey("night_mode")
    val METRIC_UNITS: Preferences.Key<Boolean> = booleanPreferencesKey("metric_units")
    // other keys of Int and Double types
}

方案1:给回调定义泛型函数类型

直接在SettingsScreenBody的回调参数中使用泛型函数类型,既保留类型安全,又能让ViewModel的泛型函数正确适配,同时子组件无需依赖ViewModel。

修改SettingsScreenBody的回调定义:

@Composable
private fun SettingsScreenBody(
    // 定义泛型函数类型的回调
    onPreferenceChange: <T> (key: Preferences.Key<T>, value: T) -> Unit,
    modifier: Modifier = Modifier,
) {
    ListPreference(
        title = R.string.pref_title_app_theme,
    ) { newValue ->
        // 编译器会自动匹配NIGHT_MODE的String类型与newValue类型,推导泛型T
        onPreferenceChange(PreferenceRepository.Keys.NIGHT_MODE, newValue)
    }
    // 其他偏好设置项示例
    SwitchPreference(
        title = R.string.pref_title_metric_units,
        checked = // 从preferences获取对应值
    ) { isChecked ->
        onPreferenceChange(PreferenceRepository.Keys.METRIC_UNITS, isChecked)
    }
}

修改后,SettingsScreen中传递viewModel::setPreferenceValue时,编译器能正确识别泛型关联,不再报错;子组件完全解耦ViewModel,预览功能正常。


方案2:用密封类封装偏好变更事件(符合单向数据流规范)

如果遵循严格的单向数据流,可定义密封类封装所有偏好变更事件,子组件仅传递事件,父组件(Screen)负责调用ViewModel处理。

  1. 定义密封类:
sealed class PreferenceEvent {
    data class UpdateNightMode(val value: String) : PreferenceEvent()
    data class UpdateMetricUnits(val value: Boolean) : PreferenceEvent()
    // 根据其他偏好类型补充对应的事件类
}
  1. 修改SettingsScreenBody的回调:
@Composable
private fun SettingsScreenBody(
    onPreferenceEvent: (PreferenceEvent) -> Unit,
    modifier: Modifier = Modifier,
) {
    ListPreference(
        title = R.string.pref_title_app_theme,
    ) { newValue ->
        onPreferenceEvent(PreferenceEvent.UpdateNightMode(newValue))
    }
    SwitchPreference(
        title = R.string.pref_title_metric_units,
        checked = // 从preferences获取对应值
    ) { isChecked ->
        onPreferenceEvent(PreferenceEvent.UpdateMetricUnits(isChecked))
    }
}
  1. 修改SettingsScreen处理事件:
@Composable
fun SettingsScreen(
    modifier: Modifier = Modifier,
    viewModel: SettingsScreenViewModel = viewModel(factory = ViewModelProvider.Factory),
) {
    Scaffold { innerPadding ->
        SettingsScreenBody(
            onPreferenceEvent = { event ->
                when(event) {
                    is PreferenceEvent.UpdateNightMode -> 
                        viewModel.setPreferenceValue(PreferenceRepository.Keys.NIGHT_MODE, event.value)
                    is PreferenceEvent.UpdateMetricUnits -> 
                        viewModel.setPreferenceValue(PreferenceRepository.Keys.METRIC_UNITS, event.value)
                    // 处理其他事件
                }
            },
            modifier = Modifier.padding(innerPadding),
        )
    }
}

这种方式事件类型更明确,子组件无需知晓具体的Preferences.Key,完全解耦ViewModel;预览时可传递空实现或测试用事件处理器。


内容的提问来源于stack exchange,提问作者BamsBamx

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.01 03:57:31