1
votes

I am currently trying to compare Java lambdas with Kotlin lambdas. I think it is a advantage that kotlin doesnt need Functional Interfaces for creating a lambda. I think it is a little bit different to get an overview of all Funtional Interfaces in Java to use lambdas and collection manipulation very well.

But I found a negative point also during my research. I think a Predicate in Java has a default function and which lets you combine some lambdas.

So you can do something like this in 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);

I think there is now equivalent in Kotlin. I read in Head first Kotlin that there is a combine function. But it doesn't work in the Kotlin playground. So I end up with an extension function. Respectively with two extension functions. One for two lambdas and another for more than two lambdas and with varargs.

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}")
}

Is it somehow easier to chain multiple lambdas? Thanks.

UPDATE

Here is an update. Thanks to @Sweeper

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

// with alias
val isAdultAndMale: PredicatePersons = {isAdult(it) && isMale(it)}
1
It is unclear what you are asking. By "chain multiple lambdas" do you mean the Java way or the Kotlin way you just invented? Also, IMO "is it somehow easier" is opinion-based. "But it doesn't work in the Kotlin playground." Can you show the code that doesn't work? - Sweeper
Sorry, my mistake. The combine function isn't an standard library function. It is a normal function which is created by the author of the book. So the question is how can I combine two lambdas like in Java? Like with an simple and? Is it possible without an extension function or is it an advantage of the SAM Predicate in Java? - Torben G
I don't think there is any built in method to do this, so you probably need an extension method. But combining them with the closure syntax (i.e. { predictate1(it) && predicate2(it) }) is concise enough, don't you think? Unlike in Java, where you need .apply all over the place. - Sweeper
Ah yes thats what I searched for :) Thank you. val isAdultAndMale: PredicatePersons = {isAdult(it) && isMale(it)} - Torben G

1 Answers

3
votes

You can define equivalents of and and or easily as extension functions:

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)