在Kotlin中,我可以覆盖一些现有的运营商,但是如何创建新的运营商呢?

在Kotlin中,我看到我可以重写一些运算符,例如+ by plus()* by函数times() …但是对于像Sets这样的一些东西,首选的(集合论)符号/运算符是不存在的。 例如A∪B为交集, A∪B为联合。

我似乎无法定义我自己的操作员,没有明确的语法来说明操作员使用什么符号。 例如,如果我想为$$作为一个运算符的函数:

 operator fun String.$$(other: String) = "$this !!whatever!! $other" // or even operator fun String.whatever(other: String) = "$this !!whatever!! $other" // how do I say this is the $$ symbol?!? 

我得到两个相同的错误:

错误:(y,x)Kotlin:“运算符”修饰符不适用于此函数:非法函数名称

什么是运营商可以创建或重写的规则?

注意: 这个问题是由作者故意写的和回答的( 自我回答的问题 ),所以对于常见的Kotlin话题的习惯性的回答是在SO中。

Kotlin只允许覆盖一组非常特定的操作符,而且不能更改可用操作符的列表。

在重写操作符时,您应该小心,试图保留原始操作符的精神或数学符号的其他常见用法。 但有时候典型的符号不可用。 例如, 可以很容易地理解为+因为它在概念上是有意义的,而且是Kotlin已经提供的一个内置的运算符Set<T>.plus() ,或者您可以创造性地使用中缀函数 :

 // already provided by Kotlin: // operator fun <T> Set<T>.plus(elements: Iterable<T>): Set<T> // and now add my new one, lower case 'u' is pretty similar to math symbol ∪ infix fun <T> Set<T>.u(elements: Set<T>): Set<T> = this.plus(elements) // and therefore use any of... val union1 = setOf(1,2,5) u setOf(3,6) val union2 = setOf(1,2,5) + setOf(3,6) val union3 = setOf(1,2,5) plus setOf(3,6) 

或者更清楚的是:

 infix fun <T> Set<T>.union(elements: Set<T>): Set<T> = this.plus(elements) // and therefore val union4 = setOf(1,2,5) union setOf(3,6) 

并继续你的Set运算符的列表,十字路口是符号所以假设每个程序员都有一个字母'n'的字体我们可以逃避:

 infix fun <T> Set<T>.n(elements: Set<T>): Set<T> = this.intersect(elements) // and therefore... val intersect = setOf(1,3,5) n setOf(3,5) 

或者通过运算符重载*作为:

 operator fun <T> Set<T>.times(elements: Set<T>): Set<T> = this.intersect(elements) // and therefore... val intersect = setOf(1,3,5) * setOf(3,5) 

尽管已经可以使用现有的标准库中缀函数intersect()如下所示:

 val intersect = setOf(1,3,5) intersect setOf(3,5) 

如果你正在发明新的东西,你需要选择最接近的操作符或函数名称。 例如否定一组枚举,也许使用-运算符( unaryMinus() )或! 运算符( not() ):

 enum class Things { ONE, TWO, THREE, FOUR, FIVE } operator fun Set<Things>.unaryMinus() = Things.values().toSet().minus(this) operator fun Set<Things>.not() = Things.values().toSet().minus(this) // and therefore use any of... val current = setOf(Things.THREE, Things.FIVE) println(-current) // [ONE, TWO, FOUR] println(-(-current)) // [THREE, FIVE] println(!current) // [ONE, TWO, FOUR] println(!!current) // [THREE, FIVE] println(current.not()) // [ONE, TWO, FOUR] println(current.not().not()) // [THREE, FIVE] 

因为操作符重载会非常有帮助,或者会导致混乱和混乱。 你必须决定什么是最好的,同时保持代码的可读性。 有时,如果操作符符合该符号的规范,或者与原始符号类似的中缀替换,或者使用描述性词语,以免混淆,那么操作符是最好的。

始终检查Kotlin Stdlib API参考,因为您需要的许多运算符可能已经被定义,或者具有等效的扩展函数。

还有一件事

而关于您的$$运营商,从技术上讲,您可以这样做:

 infix fun String.`$$`(other: String) = "$this !!whatever!! $other" 

但是因为你需要跳过这个函数的名字,所以称它为丑:

 val text = "you should do" `$$` "you want" 

这不是真正的运算符重载,只有当它是一个函数, 我可以中infix