kotlin - 如何在 Kotlin 中定义自定义赋值运算符重载?

标签 kotlin operator-overloading assignment-operator gradle-kotlin-dsl

我有一个包含可变值的 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 has a new experimental feature在 Kotlin DSL 中,它可以满足我的需求。如何在我自己的库中引入相同的赋值运算符?

我尝试查看 operator overloading docs ,但没有引用赋值运算符。

有一个KEEP language proposal for introducing such a feature ,但是it was closed .

我使用的是 Kotlin 1.8.20

最佳答案

有一个新的 Kotlin (v1.8.0) 编译器插件,可用于提供运算符加载。

尚未公布,但已可供使用。它在 Kotlin source code here 中。它与 Gradle 在 Gradle version 8.1 的 Kotlin DSL 中使用的插件相同。

IntelliJ 中的支持可能有限 - 请确保您使用的是最新版本。

应用 Kotlin 赋值编译器插件

Kotlin 分配插件可以像其他 Kotlin 编译器插件一样应用。

在 Gradle 项目中,可以使用 a simple Gradle plugin 来应用它。

我不熟悉使用 Maven、Ant 或 CLI 编译 Kotlin - 但请查看 the other Kotlin compiler plugin instructions for similar instructions

ephemient has shared CLI instructions on Slack

$KOTLIN_HOME/bin/kotlinc-jvm -Xplugin=$KOTLIN_HOME/lib/assignment-compiler-plugin.jar -P plugin:org.jetbrains.kotlin.assignment:annotation=fqdn.SupportsKotlinAssignmentOverloading ...

(you can see all the strings in https://github.com/JetBrains/kotlin/blob/master/plugins/assign-plugin/assign-plugin.common/src/org/jetbrains/kotlin/assignment/plugin/AssignmentPluginNames.kt)

设置赋值重载

首先在您的项目中创建注释

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
}

我建议通常应将注释应用于包含一个(或可能多个)可变值的“包装器”类。它也可以应用于其他类似类实现的接口(interface)(which is what Gradle does)。

编写赋值运算符

应用编译器插件并设置注释后,您就可以开始编写重载运算符。

@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)

限制

属性不能可变

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.
}

赋值重载仅适用于成员属性

请记住,赋值重载在属性为 member properties 时有效。

因此,当 StringWrapper 值不是类属性时,赋值重载器将不起作用。

val nameValue = StringWrapper()
nameValue = "123" // ERROR Val cannot be reassigned, and Type mismatch

手动调用assign()函数,或者创建一个带有属性的类。使用 object expression 也可以。

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

关于kotlin - 如何在 Kotlin 中定义自定义赋值运算符重载?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/76022932/

相关文章:

android - Kotlin 数据类的 toString 方法的混淆

sorting - 在Kotlin中对集合进行分类和分组的一种简洁方式是什么?

c++ - 操纵器的运算符重载

ios - 如何在swift中重载赋值运算符

c++ - 定义友元运算符的问题

c++ - 减少 operator= 和复制构造函数之间的代码重复

c++ - 使用 std::string 定义运算符 =

java - 插入 AssignmentOperator ArrayInitializer 错误

android - 如何获取当前设备的地区/国家?

android - 将 Android Studio 项目与 Azure Pipelines CI/CD 集成