Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

member reference in Kotlin(::)

Tags:

kotlin

class Student(val id: Int, val name: String)

fun main() {
    val list = arrayListOf<Student>(Student(200, "Lim"), Student(100, "Kim"), Student(300, "Park"))

    println(list.map { Student::name })
    println(list.map { student -> student.name })
}

Result:

[val Student.name: kotlin.String, val Student.name: kotlin.String, val Student.name: kotlin.String]
[Lim, Kim, Park]

I don't understand why it happens.

like image 584
임세준 Avatar asked Dec 31 '22 08:12

임세준


2 Answers

Let's take a look what String::name actually is. It is a KProperty1<Student, String>. This interface implements (Student) -> String as well. So, we are actually dealing with a lambda which takes a Student as parameter and returns a String.

Now, what does the map function take? The map function takes a lambda which maps some type to another ((T) -> R), which means we can pass Student::name to it. Like for any function you put the parameters inside parentheses.

list.map(Student::name) // variant 1

If you want to define your own lambda, you can do that like that:

val lambda: (Student) -> String = { it.name }
list.map(lambda) // variant 2

Notice how the lambda is passed inside the parentheses as parameter. Those parentheses can be omitted if you inline the lambda (and the lambda is the last parameter of that function):

list.map { it.name } // variant 3

Let's see what happened to you. You passed the reference inside the lambda:

list.map { Student::name }

which means that you transform any element in the list to the reference of the name property. So, what you want is go with variant 1, 2 or 3.

like image 130
Willi Mentzel Avatar answered Jan 04 '23 15:01

Willi Mentzel


As already stated in a comment, your first map operation maps a reference to the field name in the class Student, which is then printed.
Your second one maps the value of that field for each instance of Student.

You can use a field reference in order to map the values, but you have to use a slightly different syntax for it: map(...) instead of map{ ... }, see these examples:

fun main(args: Array<String>) {
    val list = arrayListOf<Student>(Student(200, "Lim"), Student(100, "Kim"), Student(300, "Park"))

    // map and print field reference of the class
    println(list.map { Student::name })

    // map and print field reference for each instance by lambda expression
    println(list.map { student -> student::name })

    // map and print values by field reference for each instance
    println(list.map(Student::name))

    // map and print values by transformation for each instance by lambda expression
    println(list.map { student -> student.name })
}

The output is therefore

[val de.os.kotlin.Student.name: kotlin.String, val de.os.kotlin.Student.name: kotlin.String, val de.os.kotlin.Student.name: kotlin.String]
[val de.os.kotlin.Student.name: kotlin.String, val de.os.kotlin.Student.name: kotlin.String, val de.os.kotlin.Student.name: kotlin.String]
[Lim, Kim, Park]
[Lim, Kim, Park]
like image 25
deHaar Avatar answered Jan 04 '23 17:01

deHaar