Kotlin 2.1.0的新改进带来哪些改变

新改进

在 Kotlin 2.1.0 版本引入了一个新的改进:Improved overload resolution for functions with generic types

官方描述,是对泛型函数的重载解析进行了加强,例如在过去:

kotlin 复制代码
class KeyValueStore<K, V> {
    fun store(key: K, value: V) {} // 1
    fun store(key: K, lazyValue: () -> V) {} // 2
}

fun <K, V> KeyValueStore<K, V>.storeExtension(key: K, value: V) {} // 1
fun <K, V> KeyValueStore<K, V>.storeExtension(key: K, lazyValue: () -> V) {} // 2

fun test(kvs: KeyValueStore<String, Int>) {
    // Member functions
    kvs.store("", 1)    // Resolves to 1
    kvs.store("") { 1 } // Resolves to 2

    // Extension functions
    kvs.storeExtension("", 1)    // Resolves to 1
    kvs.storeExtension("") { 1 } // Doesn't resolve
}

对于类中泛型函数的重载是可以正确解析的,你传递一个lambda作为参数不会被解析到 函数1,但是在扩展函数的情况下,编译器会报错提示你存在歧义的重载函数,它无法将上边例子中的 lambda 解析到 storeExtension(key: K, lazyValue: () -> V)

在 Compose 中实现 React 的 useState?

这个问题对于我们日常开发可能影响不大,但是对于库作者而言可能有些难受。例如在 ComposeHooks 中,我一直期望实现类似 React 中 useState 的效果,即 setState 函数既能接收值,也能接收一个 (oldStateValue)=>newStateValue 函数,因为这两者在逻辑上是几乎是一致的,你只需要对 lambda 求值,就可以使用同样的逻辑来执行更新状态。

在 React 中你可以这样写:

javascript 复制代码
const [state,setState] = useState(0);

setState(1); // 更新状态值为1
setState(value => value + 1); // 更新状态值加1

这种写法非常灵活,我们经常需要使用当前状态的值进行计算,将结果作为新值赋值给状态,这是一个非常常见的场景。

这一切都是因为 JavaScript 是弱类型语言,也就是解构出的 setState 既可以接收一个函数作为参数,也可以接收一个值作为参数,但是在 Kotlin 中无法实现 Kotlin 也不支持联合类型,但是这个新特性让我看到了转机。

Arrow 前来助力

arrow 是一个非常棒的 Kotlin 函数式编程库,它扩展了 Kotlin 在函数式编程上的应用场景,在 ComposeHooks 中也有很多地方运用了这个库。

这里我们主要使用的是 Either<L,R> 这个容器类型,这个类型包装了左值与右值,其结果只能是其中之一,非常类似 Kotlin 原生的 Result 类型,它提供了方便的扩展函数 left()right() 来快速的创建 Either 实例。

回到正题,我们的 setState 函数其实是这样的一个函数:

kotlin 复制代码
typealias SetValueFn<T> = (T) -> Unit

现在它只能接收一个 值 作为参数,而不能接收一个 lambda,那如果这里的 T 的类型是这样的呢:

kotlin 复制代码
Either<T, (T) -> T>

它是一个容器类型,它的左值是值,右值是一个函数,这时我们只需要为这个函数类型的调用操作符进行重载:

kotlin 复制代码
typealias SetterEither<T> = Either<T, (T) -> T>

operator fun <T> SetValueFn<SetterEither<T>>.invoke(leftValue: T) = this(leftValue.left())

operator fun <T> SetValueFn<SetterEither<T>>.invoke(rightValue: (T) -> T) = this(rightValue.right())

然后借助 Eitherfold 函数我可以方便的处理左右值:

kotlin 复制代码
GetStateHolder(
    state = state,
    setValue = { value: SetterEither<T & Any> ->
        val newValue = value.fold({ it }, { it(state.value) }) // 处理左右值情况
        state.value = newValue
    },
    getValue = { state.value }
)

左值直接取值,右值传递当前状态的值执行 lambda 进行求值。

现在你只需 import 这个 invoke 重载,就可以获得在 React 中使用 useState 一样的体验了。

kotlin 复制代码
import xyz.junerver.compose.hooks.invoke

val (state, setState) = useGetState(default)
fun set(num:Int) {
    setState(num) // 传递值
}
fun add(){
    setState{ it +1 } // 传递函数
}

查看更多

相关推荐
山河木马14 分钟前
前端学习C++之:创建对象
前端·javascript·c++
汪子熙17 分钟前
web 应用本地开发中的 LiveReload 协议深度解析
前端·javascript
UI设计和前端开发从业者18 分钟前
大数据时代UI前端的智能化转型之路:以数据为驱动的产品创新
大数据·前端·ui
全宝32 分钟前
前端也能这么丝滑!Node + Vue3 实现 SSE 流式文本输出全流程
前端·javascript·node.js
前端小巷子1 小时前
Web缓存:原理、策略与优化
前端·面试
小磊哥er1 小时前
【前端工程化】前端工作中如何协同管理开发任务?
前端
程序员小白条1 小时前
我的第二份实习,学校附近,但是干前端!
java·开发语言·前端·数据结构·算法·职场和发展
编程大全1 小时前
2025年前端面试题
前端
萌萌哒草头将军1 小时前
🔥🔥🔥 NuxtLabs 宣布加入了 Vercel !
前端·javascript·vue.js
LuciferHuang8 小时前
震惊!三万star开源项目竟有致命Bug?
前端·javascript·debug