【问题标题】:In Kotlin, I can override some existing operators but what about creating new operators?在 Kotlin 中,我可以覆盖一些现有的运算符,但是创建新的运算符呢?
【发布时间】:2017-01-17 13:37:12
【问题描述】:

在 Kotlin 中,我看到我可以通过函数 plus() 覆盖一些运算符,例如 +,通过函数 * 覆盖 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?!?

我得到了同样的错误:

Error:(y, x) Kotlin: 'operator' 修饰符不适用于此函数:非法函数名

可以创建或覆盖哪些运算符的规则是什么?

注意: 此问题由作者 (Self-Answered Questions) 有意编写和回答,因此常见的 Kotlin 主题的惯用答案出现在 SO 中。

【问题讨论】:

    标签: operator-overloading kotlin


    【解决方案1】:

    Kotlin 只允许一组非常具体的 operators to be overridden,而您不能更改可用运算符的列表。

    在重写运算符时应小心,您尝试保持原始运算符的精神,或数学符号的其他常见用法。但有时典型的符号不可用。例如 set Union 可以很容易地视为 + 因为从概念上讲它是有意义的,这是 Kotlin 已经提供的内置运算符 Set<T>.plus(),或者您可以发挥创意并在这种情况下使用 infix function

    // 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 Reference,因为您需要的许多运算符可能已经定义,或者具有等效的扩展功能。

    另一件事......

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

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

    但是因为需要对函数名进行转义,调用起来会很丑:

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

    这并不是真正的运算符重载,只有当它是 can me made infix 的函数时才会起作用。

    【讨论】:

      猜你喜欢
      • 2011-03-20
      • 1970-01-01
      • 2010-11-05
      • 1970-01-01
      • 2014-10-16
      • 1970-01-01
      • 2018-08-22
      • 1970-01-01
      • 2012-01-15
      相关资源
      最近更新 更多