Выбор ссылки на функцию перед применением: проблема с выводом типа - PullRequest
0 голосов
/ 07 февраля 2019

Я хочу выбрать ссылку на функцию перед ее применением к аргументам, но Котлин не может определить ее тип.

Предположим, у меня есть класс

class Circle(val radius: Float)

и функция

fun sortByRadius(circles: MutableList<Circle>, ascending: Boolean) {
    if (ascending)
        circles.sortBy { it.radius }
    else
        circles.sortByDescending { it.radius }
}

Я тоже хочу переписать это тело функции примерно так:

circles.(
    if (ascending) MutableList<Circle>::sortBy
    else MutableList<Circle>::sortByDescending
) { it.radius }

, но оно не работает.Также я узнал, что

(MutableList<Circle>::sortBy)(circles) { it.radius }

почти работает, Котлин просто не может вывести Float тип радиуса;поэтому мне интересно, как это указать.Тогда мы могли бы написать

(if (ascending) MutableList<Circle>::sortBy
    else MutableList<Circle>::sortByDescending)(circles) { it.radius }

Ответы [ 2 ]

0 голосов
/ 04 марта 2019

Хороший вопрос, и он кажется невозможным (используя ссылки на функции).Я попытался указать ожидаемый тип явно:

val f: MutableList<Circle>.((Circle) -> Float) -> Unit = 
    if (ascending) MutableList<Circle>::sortBy else MutableList<Circle>::sortByDescending

, но в сообщении об ошибке говорится:

Ошибка вывода типа.Пожалуйста, попробуйте явно указать аргументы типа.

и грамматика не позволяет этого:

callableReference
  : (receiverType? '::' (simpleIdentifier | 'class'))
  ;

т.е. за :: может следовать толькоидентификатор или class.Это странная дыра, потому что грамматика Java допускает параметры типа перед именем метода.

Связанные ссылки тоже не помогают:

(circles::sortBy) { it.radius } // doesn't compile

Не работает даже явный тип, что меня довольно удивило:

val f: ((Circle) -> Float) -> Unit = circles::sortBy 

но это так:

val f: ((Circle) -> Float) -> Unit = { circles.sortBy(it) }

Таким образом, в конечном итоге вы можете написать (скобки вокруг лямбды обязательны)

val f: ((Circle) -> Float) -> Unit = 
  if (ascending) ({ circles.sortBy(it) }) else ({ circles.sortByDescending(it) })
f { it.radius }

, но, вероятно, не хотите!

0 голосов
/ 21 февраля 2019

Не знаю, полностью ли я понимаю вашу точку зрения, но вот что я написал для вашего случая:

fun main() {
    val circles = mutableListOf(
        Circle(.1f), Circle(.5f), Circle(3f)
    )

    val sortBy: (MutableList<Circle>, selector: (Circle) -> Float) -> Unit = { list, selector ->
        list.sortBy(selector)
    }

    val sortByDescending: (MutableList<Circle>, selector: (Circle) -> Float) -> Unit = { list, selector ->
        list.sortByDescending(selector)
    }

    val ascending = false
    (if (ascending) sortBy else sortByDescending)(circles) {
        it.radius
    }
    // Or maybe more clear way
    //(if (ascending) sortBy else sortByDescending).invoke(circles) {
    //    it.radius
    //}
}

Надеюсь, это поможет вам.

...