Kotlin provides a concise way to reference a member of a class or an instance of a class without invoking it, called member reference. Member reference is a functional feature in Kotlin that allows you to pass a function reference as a parameter to another function, without actually invoking the function. It’s similar to method reference but works with properties and functions that are members of a class or an instance of a class.
In this article, we’ll explore how to use member reference in Kotlin, including syntax, examples, and use cases.
What is a Member Reference?
A member reference in Kotlin is a way to reference a member of a class or interface, such as a property or a method, without invoking it immediately. It is similar to a lambda expression, but instead of providing a block of code to execute, it provides a reference to a member of a class. Member references are useful when you want to pass a function or a property as an argument to another function or class constructor.
Kotlin provides two types of member references: property references and function references. Property references refer to properties of a class, while function references refer to methods of a class.
Property References
Property references allow you to reference a property of a class without invoking it immediately. You can create a property reference by prefixing the property name with the double colons (::) operator. For example, consider the following class:
class Person(val name: String, val age: Int)
To create a property reference for the name
property, you can use the following syntax:
val getName = Person::name
In this example, the getName
variable is a property reference to the name
property of the Person
class. You can use this property reference in many contexts, such as passing it as an argument to a function:
fun printName(getName: (Person) -> String, person: Person) {
println(getName(person))
}
val person = Person("Amol Pawar", 20)
printName(Person::name, person)
In this example, the printName
function takes a property reference to the name
property of the Person
class and a Person
object. It then uses the property reference to print the name of the person.
Function References
Function references allow you to reference a method of a class without invoking it immediately. You can create a function reference by prefixing the method name with the double colons (::) operator. For example, consider the following class:
class Calculator {
fun add(a: Int, b: Int): Int {
return a + b
}
}
To create a function reference for the add
method, you can use the following syntax:
val calculator = Calculator()
val add = calculator::add
In this example, the add
variable is a function reference to the add
method of the Calculator
class. You can use this function reference in many contexts, such as passing it as an argument to a function:
fun performOperation(operation: (Int, Int) -> Int, a: Int, b: Int) {
val result = operation(a, b)
println("Result: $result")
}
val calculator = Calculator()
performOperation(calculator::add, 5, 10)
In this example, the performOperation
function takes a function reference to the add
method of the Calculator
class and two integer values. It then uses the function reference to perform the addition operation and print the result.
Member References with Bound Receivers
In some cases, you may want to use a member reference with a bound receiver. A bound receiver is an instance of a class that is associated with the member reference. To create a member reference with a bound receiver, you can use the following syntax:
val calculator = Calculator()
val add = calculator::add
In this example, the add
variable is a function reference to the add
method of the Calculator
class, with a bound receiver of the calculator
instance.
You can use a member reference with a bound receiver in many contexts, such as passing it as an argument to a function:
fun performOperation(operation: Calculator.(Int, Int) -> Int, calculator: Calculator, a: Int, b: Int) {
val result = calculator.operation(a, b)
println("Result: $result")
}
val calculator = Calculator()
performOperation(Calculator::add, calculator, 5, 10)
In this example, the performOperation
function takes a function reference to the add
method of the Calculator
class, with a bound receiver of the Calculator
class. It also takes a Calculator
instance and two integer values. It then uses the function reference with the calculator
instance to perform the addition operation and print the result.
Use Cases for Member Reference
Member reference can be used in many different contexts, such as passing functions as parameters to higher-order functions or creating a reference to a member function or property for later use. Here are some examples of how to use member reference in Kotlin.
1. Passing a member function as a parameter
One of the most common use cases for member reference is passing a member function as a parameter to a higher-order function. Higher-order functions are functions that take other functions as parameters or return functions as results. By passing a member function as a parameter, you can reuse the same functionality across different contexts.
class MyClass {
fun myFunction(param: Int) {
// function implementation
}
}
fun higherOrderFunction(func: (Int) -> Unit) {
// do something
}
fun main() {
val myClassInstance = MyClass()
higherOrderFunction(myClassInstance::myFunction)
}
In this example, we have a class called MyClass
with a member function called myFunction
. We then create an instance of MyClass
and store it in the myClassInstance
variable. Finally, we pass a reference to the myFunction
function using member reference to the higherOrderFunction
, which takes a function with a single Int
parameter and returns nothing.
2. Creating a reference to a member function for later use
Another use case for member reference is creating a reference to a member function that can be invoked later. This can be useful if you want to invoke a member function on an instance of a class without actually calling the function immediately.
class MyClass {
fun myFunction(param: Int) {
// function implementation
}
}
fun main() {
val myClassInstance = MyClass()
val functionReference = myClassInstance::myFunction
// ...
functionReference.invoke(42) // invoke the function later
}
In this example, we have a class called MyClass
with a member function called myFunction
. We then create an instance of MyClass
and store it in the myClassInstance
variable. Finally, we create a reference to the myFunction
function using the double colon operator and store it in the functionReference
variable. We can then use the invoke
function to call the myFunction
function on the myClassInstance
object later.
3. Creating a reference to a member property for later use
In addition to member functions, you can also create a reference to a member property for later use. This can be useful if you want to access a member property on an instance of a class without actually accessing the property immediately.
class MyClass {
var myProperty: String = ""
}
fun main() {
val myClassInstance = MyClass()
val propertyReference = myClassInstance::myProperty
// ...
propertyReference.set("softAai") // set the property later
val value = propertyReference.get() // get the property later
}
In this example, we have a class called MyClass
with a member property called myProperty
. We then create an instance of MyClass
and store it in the myClassInstance
variable. Finally, we create a reference to the myProperty
property using the double colon operator and store it in the propertyReference
variable. We can then use the set
and get
functions to access the myProperty
property on the myClassInstance
object later.
4. Bound member reference
Bound member reference is a syntax for referencing a member function of a specific instance of a class. This is useful when you have a function that expects a specific instance of a class as a parameter.
class MyClass {
fun myFunction(param: Int) {
// function implementation
}
}
fun main() {
val myClassInstance = MyClass()
val boundReference = myClassInstance::myFunction
// ...
boundReference(42) // call the function on myClassInstance
}
In this example, we have a class called MyClass
with a member function called myFunction
. We then create an instance of MyClass
and store it in the myClassInstance
variable. Finally, we create a bound reference to the myFunction
function using the double colon operator and store it in the boundReference
variable. We can then call the myFunction
function on the myClassInstance
object later by simply invoking the boundReference
function and passing in the necessary parameters.
Member References and Lambdas
In Kotlin, lambda expressions and member references can be used interchangeably in certain situations. This is because a lambda expression that takes an object and calls one of its methods can be replaced with a reference to that method using the double colon operator. This makes the code more concise and readable.
For example, consider the following code:
val myList = listOf("abc", "opq", "xyz")
val lengthList = myList.map { str -> str.length }
In this code, we have a list of strings and we want to create a new list containing the lengths of each string in the original list. We achieve this using the map
function and a lambda expression that takes a string and returns its length.
Now, consider the following code, which achieves the same thing but uses a member reference instead of a lambda expression:
val myList = listOf("abc", "opq", "xyz")
val lengthList = myList.map(String::length)
In this code, we use a member reference to reference the length
function of the String
class instead of the lambda expression. This is possible because the lambda expression only calls a single method on the object it receives, which is exactly what the member reference does.
Let’s take one more example, let’s say you have a class called Person
with a method getName()
that returns the person\’s name. You can define a lambda expression that calls this method as follows:
val p = Person("amol")
val getNameLambda: (Person) -> String = { person -> person.getName() }
Alternatively, you can define a callable reference (method reference) to the same method as follows:
val getNameRef: (Person) -> String = Person::getName
In this case, getNameRef
is a callable reference to the getName()
method of the Person
class, and it has the same type as getNameLambda
. You can use either getNameLambda
or getNameRef
interchangeably in contexts where a function or method of type (Person) -> String
is expected.
This interchangeability between lambdas and member references can be useful in situations where you have a lambda expression that only calls a single method on an object, as it can make the code more concise and readable. However, it’s important to note that this interchangeability only works in these specific situations and there may be cases where a lambda expression or a member reference is more appropriate.
Conclusion
Kotlin member references provide a concise and readable way to reference a class’s properties or methods, without invoking them immediately. They are useful when you want to pass a function or a property as an argument to another function or class constructor. Kotlin provides two types of member references: property references and function references. Property references refer to properties of a class, while function references refer to methods of a class. You can also create member references with bound receivers, which allows you to associate a member reference with a specific instance of a class. With member references, Kotlin makes it easy to write concise and readable code that is easy to maintain and understand.