Как сочетать лямбды в Котлине

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

Но во время исследования я обнаружил и отрицательный момент. Я думаю, что у Predicate в Java есть функция по умолчанию, которая позволяет комбинировать некоторые лямбды.

Итак, вы можете сделать что-то вроде этого на Java:

final Predicate<Person> isAdult = person -> person.getAge() >= 18;
final Predicate<Person> isMale = person -> person.getGender() == Gender.MALE;

// Combine 
final Predicate<Person> isAdultAndMale = isAdult.and(isMale);

Я думаю, что сейчас есть эквивалент в Котлине. Я прочитал в Head first Kotlin, что есть функция комбинирования. Но на детской площадке Kotlin это не работает. Так что я получаю функцию расширения. Соответственно с двумя функциями расширения. Один для двух лямбд, а другой для более чем двух лямбд и с вараргами.

enum class Gender{MALE, FEMALE}
enum class EyeColor{BLUE, GREEN}

data class Person(
    var name: String, 
    var age: Int, 
    var gender: Gender,
    var eyeColor: EyeColor
)

fun List<Person>.combineTwoLambdas(firstLambda: (Person) -> Boolean, secondLambda: (Person) -> Boolean): List<Person> {
    return this.filter(firstLambda).filter(secondLambda)
}

fun List<Person>.combineMoreLambdas(vararg personFilters: (Person) -> Boolean): List<Person> {
    var myPersons = listOf<Person>()

    personFilters.forEach {
        myPersons = this.filter(it)
    }

    return myPersons
}

typealias PredicatePersons = (Person) -> Boolean

fun main() {
    val persons = listOf(
        Person("Susi", 20, Gender.FEMALE, EyeColor.BLUE), 
        Person("Ralf", 19, Gender.MALE, EyeColor.BLUE),
        Person("Michael", 20, Gender.MALE, EyeColor.GREEN))

    val isAdult: (Person) -> Boolean = {person -> person.age >= 18}
    val isMale: (Person) -> Boolean = {it.gender == Gender.MALE}
    val hasGreenEyes: PredicatePersons = {it.eyeColor == EyeColor.GREEN}

    // combine two lambdas
    val isAdultAndMale = persons.combineTwoLambdas(isAdult, isMale)

    // combine more than two lambdas
    val isAdultAndMaleAndHasGreenEyes = persons.combineMoreLambdas(isAdult, isAdult, hasGreenEyes)


    print("combine all ${isAdultAndMaleAndHasGreenEyes}")
}

Как-то проще связать несколько лямбд? Спасибо.

ОБНОВЛЕНИЕ

Вот обновление. Спасибо @Sweeper

val isAdultAndMale: (Person) -> Boolean = {isAdult(it) && isMale(it)}

// with alias
val isAdultAndMale: PredicatePersons = {isAdult(it) && isMale(it)}

person Torben G    schedule 09.04.2020    source источник
comment
Непонятно, о чем вы спрашиваете. Под цепочкой нескольких лямбд вы подразумеваете способ Java или способ Kotlin, который вы только что изобрели? Кроме того, ИМО это как-то проще основано на мнениях. Но на детской площадке Kotlin это не работает. Вы можете показать код, который не работает?   -  person Sweeper    schedule 09.04.2020
comment
Извините моя ошибка. Функция объединения не является стандартной библиотечной функцией. Это нормальная функция, созданная автором книги. Итак, вопрос в том, как я могу объединить две лямбды, как в Java? Как с простым и? Возможно ли это без функции расширения или это преимущество предиката SAM в Java?   -  person Torben G    schedule 09.04.2020
comment
Я не думаю, что для этого есть какой-либо встроенный метод, поэтому вам, вероятно, понадобится метод расширения. Но комбинировать их с синтаксисом закрытия (т.е. { predictate1(it) && predicate2(it) }) достаточно лаконично, вам не кажется? В отличие от Java, где вам нужно .apply повсюду.   -  person Sweeper    schedule 09.04.2020
comment
Ах да, это то, что я искал :) Спасибо. val isAdultAndMale: PredicatePersons = {isAdult (it) && isMale (it)}   -  person Torben G    schedule 09.04.2020


Ответы (1)


Вы можете легко определить эквиваленты and и or как функции расширения:

fun <T> ((T) -> Boolean).and(arg: (T) -> Boolean): (T) -> Boolean = { this(it) && arg(it) }
fun <T> ((T) -> Boolean).or(arg: (T) -> Boolean): (T) -> Boolean = { this(it) || arg(it) }

...
val isAdultAndMale = isAdult.and(isMale)
person Alexey Romanov    schedule 09.04.2020