Инфиксная нотация представляет помещение оператора или функции перед операндами или аргументами. Инфиксная нотация часто применяется для определения
математических и логических выражений. К примеру, выражение (+ a b)
представляет инфиксную нотацию записи суммы чисел a + b
.
Для определения инфиксной функции вначале ее определения указывается ключевое слово infix:
infix fun название_функции(параметр: тип_параметра): тип_возвращаемого_значения{ // действия функции }
Инфиксная функция должна принимать только один параметр. При этом параметр не должен иметь значение по умолчанию и не должен представлять неопределенный набор значений.
Есть два способа определения инфиксной функции: либо внутри класса, либо как функции расширения.
Определим вначале внутри класса:
fun main() { val acc = Account(1000) acc put 150 // равноценно вызову acc.put(150) acc.printSum() // 1300 } class Account(var sum: Int) { infix fun put(amount: Int){ sum = sum + amount } fun printSum() = println(sum) }
Здесь определен класс Account - класс банковского счета, который через конструктор принимает начальную сумму на счете. С помощью инфиксной функции
put()
определяем добавление на счет суммы, переданной через параметр функции.
Вызов функции выглядит следующим образом:
acc put 150
Первый параметр (здесь переменная acc
) представляет объект, который вызывает функцию. А второй параметр - данные, которые непосредственно
будут передаваться инфиксной функции через ее параметр. То есть данный вызов фактически аналогичен вызову:
acc.put(150)
Также инфиксная функция может определяться как функция расширения. Например, перепишем выше использованную функцию put()
в виде функции расширения:
fun main() { val acc = Account(1000) acc put 150 acc.put(150) acc.printSum() // 1300 } infix fun Account.put(amount: Int){ this.sum = this.sum + amount } class Account(var sum: Int) { fun printSum() = println(sum) }
Стоит отметить, что функция расширения в отличие от функции внутри класса имеет доступ только тем свойствам, которые являются публичными.
Однако использование функций расширений позволяет добавить инфиксные функции к уже существующим типам. Например, определим инфиксную функцию для подсчета частоты символа в строке:
fun main() { val hello = "hello world" val lCount = hello wordCount 'l' val oCount = hello wordCount 'o' println(lCount) // 3 println(oCount) // 2 } infix fun String.wordCount(c: Char) : Int{ var count = 0 for(n in this){ if(n == c) count++ } return count }
Здесь функция wordCount
проходит по всем символам строки и подсчитывает, сколько раз встречается символ, передаваемый через параметр функции.
Результат возвращается функцией. Затем мы можем применить инфиксную нотацию:
val lCount = hello wordCount 'l'
Поскольку функция возвращает результат типа Int, то мы можем получить этот результат в переменную.
Инфиксная функция позволяет задать способ описания действия, более близкий к естественному языку. Например:
fun main() { val tom = Person("Tom") tom says "Hello" } class Person(val name: String) { infix fun says(words: String){ println("$name says: ${words}") } }
Здесь определен класс Person, который представляет человека и который через параметр name конструктора получает имя человека. В этом классе в виде инфиксной функции определен метод "says", который принимает параметр words - это условные слова, которые произносит человек.
Для вызова метода says мы можем написать следующим образом:
tom says "Hello"
что может показаться более естественным выражения действия на английском, нежели:
tom.says("Hello")
И не только на английском. Например:
fun main() { val Том = Person("Tom") Том говорит "Привет" } class Person(val name: String) { infix fun говорит(words: String){ println("$name говорит: ${words}") } }