В Kotlin я могу переопределить некоторые существующие операторы, но как насчет создания новых операторов?

в Котлине я вижу, что могу переопределить некоторые операторы, такие как + функции plus() и * функции times() ... но для некоторых вещей, таких как множества, предпочтительные (теория множеств) символы/операторы не существуют. Например 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: модификатор "оператор" неприменим к этой функции: незаконное имя функции

каковы правила для того, какие операторы могут быть созданы или переопределены?

Примечание: этот вопрос намеренно написан и на него отвечает автор (Вопросы С Ответами), так что идиоматические ответы на часто задаваемые Темы Котлина присутствуют в SO.

1 ответов


Котлин позволяет только очень конкретный набор операторы для переопределения и не может изменить список доступных операторов.

вы должны позаботиться при переопределении операторов, которые вы пытаетесь сохранить в духе исходного оператора или других общих применений математического символа. Но иногда типичный символ недоступен. Например, установите Union можно легко рассматривать как + потому что концептуально это делает смысл и это встроенный оператор Set<T>.plus() уже предоставлено Kotlin, или вы можете получить творческий подход и использовать функция infix для этого случая:

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

и продолжая свой список операторов, перекресток-это символ Итак, предполагая, что у каждого программиста есть шрифт, где буква " 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)

или через оператора перегрузка * as:

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)

хотя вы уже можете использовать существующую стандартную функцию библиотеки infix intersect() as:

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]

будьте внимательны, так как перегрузка оператора может быть очень полезная, или это может привести к путанице и хаосу. Вы должны решить, что лучше всего, сохраняя читаемость кода. Иногда оператор лучше всего подходит, если он соответствует норме для этого символа или замене инфикса, аналогичной исходному символу, или использует описательное слово, чтобы не было возможности путаницы.

всегда проверяйте ссылка на API Kotlin Stdlib потому что многие операторы, которые вы хотите, могут быть уже определены или иметь эквивалентное расширение функции.

еще одна вещь...

и о $$ оператора, технически вы можете сделать это так:

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

но поскольку вам нужно избежать имени функции, будет некрасиво вызывать:

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

это не действительно перегрузка оператора и будет работать, только если это функция, которая может мне сделать infix.