提问人:aSemy 提问时间:4/15/2023 更新时间:4/16/2023 访问量:281
如何在 Kotlin 中定义自定义分配运算符重载?
How can I define a custom assign operator overload in Kotlin?
问:
我有一个包装可变值的 Kotlin 类。
class StringWrapper(
var value: String = ""
) {
override fun toString(): String = value
}
我使用这个包装器作为自定义数据持有者类中的属性
class DataHolder {
val name = StringWrapper()
override fun toString(): String = "Data(name=$name)"
}
我想更轻松地为StringWrapper
val dataAlpha = DataHolder()
// the '.value' is unnecessary noise
dataAlpha.name.value = "alpha"
// I want to directly assign a string value, but I get an error
dataAlpha.name = "alpha" // ERROR Type mismatch.
// Required: StringWrapper
// Found: String
我还想让一个更容易复制到另一个。StringWrapper
val dataAlpha = DataHolder()
dataAlpha.name = "alpha"
val dataAlphaCopy = DataHolder()
// I want to directly assign the value of `dataAlpha.name.value` into `dataAlphaCopy.name.value`
dataAlphaCopy.name = dataAlpha.name // ERROR Val cannot be reassigned
我知道 Gradle 8.1 在 Kotlin DSL 中有一个新的实验性功能,可以做我想要的。如何在我自己的库中引入相同的赋值运算符?
我试过查看运算符重载文档,但没有引用赋值运算符。
有一个 KEEP 语言提案用于引入此类功能,但它已关闭。
我正在使用 Kotlin 1.8.20
答:
有一个新的 Kotlin (v1.8.0) 编译器插件可用于提供运算符加载。
它尚未公布,但可以使用。它位于此处的 Kotlin 源代码中。它与 Gradle 在 Gradle 8.1 版的 Kotlin DSL 中使用的插件相同。
IntelliJ 中的支持可能有限 - 请确保您使用的是最新版本。
应用 Kotlin Assignment 编译器插件
Kotlin Assignment 插件可以像其他 Kotlin 编译器插件一样应用。
在 Gradle 项目中,可以使用简单的 Gradle 插件来应用它。
我不熟悉使用 Maven、Ant 或通过 CLI 编译 Kotlin - 但请查看其他 Kotlin 编译器插件说明以获取类似说明。
$KOTLIN_HOME/bin/kotlinc-jvm -Xplugin=$KOTLIN_HOME/lib/assignment-compiler-plugin.jar -P plugin:org.jetbrains.kotlin.assignment:annotation=fqdn.SupportsKotlinAssignmentOverloading ...
设置分配重载
首先在项目中创建注释
package my.project
/** Denotes types that will be processed by the Kotlin Assignment plugin */
@MustBeDocumented
@Retention(AnnotationRetention.RUNTIME)
@Target(AnnotationTarget.CLASS)
annotation class KotlinAssignmentOverloadTarget
然后在 Gradle 中应用插件,并将其配置为使用您的注释
plugins {
kotlin("plugin.assignment") version "1.8.10"
}
assignment {
annotation("my.project.KotlinAssignmentOverloadTarget")
}
然后在代码中将注解应用于 。StringWrapper
@KotlinAssignmentOverloadTarget
class StringWrapper(
var value: String = ""
) {
override fun toString(): String = value
}
我建议通常应将注释应用于包含一个(或可能多个)可变值的“包装器”类。它也可以应用于其他类似类实现的接口(Gradle 就是这样做的)。
编写赋值运算符
应用编译器插件并设置注解后,就可以开始编写重载运算符了。
@KotlinAssignmentOverloadTarget
class StringWrapper(
var value: String = ""
) {
// member function
/** Provides overloaded setter for setting the value of [value] using an assignment syntax */
fun assign(value: String) {
this.value = value
}
}
// extension function
/** Provides overloaded setter for setting the value of [value] using an assignment syntax */
fun StringWrapper.assign(value: StringWrapper) {
this.value = value.value
}
使用赋值运算符
现在,您可以直接将字符串分配给该属性name
val dataAlpha = DataHolder()
dataAlpha.name = "alpha"
println(dataAlpha) // prints: Data(name=alpha)
并且,使用扩展功能,将一个分配给另一个。StringWrapper
val dataAlphaCopy = DataHolder()
dataAlphaCopy.name = dataAlpha.name
println(dataAlphaCopy) // prints: Data(name=alpha)
局限性
属性不能是可变的
当 s 类型的属性为时,运算符重载将不起作用。它们必须是 s。StringWrapper
var
val
class MutableDataHolder {
var name = StringWrapper()
}
fun main() {
val dataAlpha = MutableDataHolder()
// no overload operator is generated, because name is a 'var'
dataAlpha.name = "alpha" // ERROR Type mismatch.
}
分配重载仅适用于成员属性
请记住,仅当属性是成员属性时,赋值重载才有效。
因此,当存在不是类属性的值时,赋值重载器将不起作用。StringWrapper
val nameValue = StringWrapper()
nameValue = "123" // ERROR Val cannot be reassigned, and Type mismatch
手动调用该函数,或创建具有属性的类。使用对象表达式也有效。assign()
val nameValue = StringWrapper()
// manually call the 'assign' function
nameValue.assign("123")
val values = object {
val name = StringWrapper()
}
values.name = "123" // 'name' is a member property, so the assignment overload works
评论