Kotlin函数传递问题:Jetpack Compose组件参数类型不匹配
我正在用Jetpack Compose和Kotlin开发Android原生项目,遇到两个Kotlin语法问题,和Jetpack无关。我想做一个通用组件TwistyCard,适配所有继承同一父类的对象,通过传入不同函数处理差异。组件的函数签名如下:
fun TwistyCard( cardItem: CardItem, childItemList: CardItemList? = null, triggerRerenderFunc: () -> Unit = {}, checkedFunc: () -> Boolean = { false }, clickExpandFunc: (CardItem, CardItemList?) -> Unit = { cardItemToExpand: CardItem, cardItemListToExpand: CardItemList? -> }, ) { // 组件实现 }
疑问一:为什么clickExpandFunc的默认值不能直接写{}?
我尝试把clickExpandFunc的默认值设为{}时会报语法错误,必须写成带参数声明的形式才能正常编译,这是为什么?
疑问二:调用时传入自定义函数出现类型不匹配错误
我写了一个自定义的clickExpand函数:
fun clickExpand(cardItem: CardItem, cardItemList: CardItemList?) { if (CardItemList.isEmpty(cardItemList)) { return } val locationType = (cardItemList!!.itemList.first() as Location).type // 其他逻辑 }
调用TwistyCard时传入这个函数的代码如下:
TwistyCard( cardItem = game, childItemList = childItemList, triggerRerenderFunc = { locationViewModel.triggerLocationListUpdate(true) }, checkedFunc = { areAllLocationsForGameChosen(gameName, locationType) }, clickExpandFunc = { clickExpand(game, childItemList) }, // 这里报语法错误 )
报错信息:
Expected 2 parameters of types CardItem, CardItemList? Type mismatch: inferred type is () -> Unit but (CardItem, CardItemList?) -> Unit was expected
系统认为我传入的是() -> Unit类型,但我明明传的是符合参数要求的函数,该怎么修改?
解答
针对疑问一:默认值不能写{}的原因
Kotlin中,函数类型(CardItem, CardItemList?) -> Unit要求lambda表达式必须接收两个参数。直接写{}的话,这个lambda没有声明任何参数,Kotlin会推断它的类型是() -> Unit,和clickExpandFunc要求的函数类型不匹配,所以报错。
你现在写的默认值是正确的:它显式声明了两个参数,且无执行逻辑,完全符合目标函数类型要求。另外也可以用更简洁的写法,利用类型推断省略参数类型:
clickExpandFunc: (CardItem, CardItemList?) -> Unit = { _, _ -> }
这里_表示不需要使用这两个参数,和原写法效果一致。
针对疑问二:类型不匹配的解决方法
你传入的{ clickExpand(game, childItemList) }是一个无参数lambda,它直接调用clickExpand并传入外部变量game和childItemList,因此被推断为() -> Unit,和clickExpandFunc要求的类型不匹配。有两种修改方式:
方式一:直接传递函数引用
如果clickExpand的参数类型和clickExpandFunc要求的完全一致,直接用::clickExpand传递函数引用即可:
TwistyCard( cardItem = game, childItemList = childItemList, triggerRerenderFunc = { locationViewModel.triggerLocationListUpdate(true) }, checkedFunc = { areAllLocationsForGameChosen(gameName, locationType) }, clickExpandFunc = ::clickExpand )
方式二:用lambda接收参数并转发
如果需要额外处理或捕获外部变量,让lambda接收TwistyCard传递的参数再转发给clickExpand:
TwistyCard( cardItem = game, childItemList = childItemList, triggerRerenderFunc = { locationViewModel.triggerLocationListUpdate(true) }, checkedFunc = { areAllLocationsForGameChosen(gameName, locationType) }, clickExpandFunc = { cardItem, itemList -> clickExpand(cardItem, itemList) } )
这里lambda接收的两个参数正好匹配clickExpandFunc的类型要求,再把参数传给自定义函数即可。
内容的提问来源于stack exchange,提问作者Dave Westerman

