If you are an experienced Android application developer, you're probably used to the verbosity of Java 7. As a result, you might be finding Kotlin's concise syntax, which is geared towards functional programmers, slightly unsettling.
One common problem beginners encounter while learning Kotlin is understanding how it expects you to work with Java interfaces that contain a single method. Such interfaces are ubiquitous in the Android world and are often referred to as SAM interfaces, where SAM is short for Single Abstract Method.
In this short tutorial, you'll learn everything you need to know to aptly use Java's SAM interfaces in Kotlin code.
1. What Is a SAM Conversion?
When you want to make use of a Java interface containing a single method in your Kotlin code, you don't have to manually create an anonymous class that implements it. Instead, you can use a lambda expression. Thanks to a process called SAM conversion, Kotlin can transparently convert any lambda expression whose signature matches that of the interface's single method into an instance of an anonymous class that implements the interface.
For example, consider the following one-method Java interface:
public interface Adder { public void add(int a, int b); }
A naive and Java 7-like approach to using the above interface would involve working with an object
expression and would look like this:
// Creating instance of an anonymous class // using the object keyword val adder = object : Adder { override fun add(a: Int, b: Int): Int { return a + b } }
That's a lot of unnecessary code, which is also not very readable. By leveraging Kotlin's SAM conversion facility, however, you can write the following equivalent code instead:
// Creating instance using a lambda val adder = Adder { a, b -> a + b }
As you can see, we've now replaced the anonymous class with a short lambda expression, which is prefixed with the name of the interface. Note that the number of arguments the lambda expression takes is equal to the number of parameters in the signature of the interface's method.
2. SAM Conversions in Function Calls
While working with Java classes having methods that take SAM types as their arguments, you can further simplify the above syntax. For example, consider the following Java class, which contains a method that expects an object implementing the Adder
interface:
public class Calculator { private Adder adder; public void setAdder(Adder adder) { this.adder = adder; } public void add(int a, int b) { Log.d("CALCULATOR", "Sum is " + adder.add(a,b)); } }
In your Kotlin code, you can now directly pass a lambda expression to the setAdder()
method, without prefixing it with the name of the Adder
interface.
val calculator = Calculator() calculator.setAdder({ a, b -> a+b })
It is worth noting that while calling a method that takes a SAM type as its only argument, you are free to skip the parenthesis to make your code even more concise.
calculator.setAdder { a, b -> a+b }
3. SAM Conversions Without Lambdas
If you think lambda expressions are confusing, I've got good news for you: SAM conversions work just fine with ordinary functions too. For example, consider the following function whose signature matches that of the Adder
interface's method:
fun myCustomAdd(a:Int , b:Int):Int = if (a+b < 100) -1 else if (a+b < 200) 0 else a+b
Kotlin allows you to directly pass the myCustomAdd()
function as an argument to the setAdder()
method of the Calculator
class. Don't forget to reference the method using the ::
operator. Here's how:
calculator.setAdder (this::myCustomAdd)
4. The it
Variable
Many times, SAM interfaces contain one-parameter methods. A one-parameter method, as its name suggests, has only one parameter in its signature. While working with such interfaces, Kotlin allows you to omit the parameter in your lambda expression's signature and use an implicit variable called it
in the expression's body. To make things clearer, consider the following Java interface:
public interface Doubler { public int doubleIt(int number); }
While using the Doubler
interface in your Kotlin code, you don't have to explicitly mention the number
parameter in your lambda expression's signature. Instead, you can simply refer to it as it
.
// This lambda expression using the it variable val doubler1 = Doubler { 2*it } // is equivalent to this ordinary lambda expression val doubler2 = Doubler { number -> 2*number }
5. SAM Interfaces in Kotlin
As a Java developer, you might be inclined to create SAM interfaces in Kotlin. Doing so, however, is usually not a good idea. If you create a SAM interface in Kotlin, or create a Kotlin method that expects an object implementing a SAM interface as an argument, the SAM conversion facility will not be available to you—SAM conversion is a Java-interoperability feature and is limited to Java classes and interfaces only.
Because Kotlin supports higher-order functions—functions that can take other functions as arguments—you'll never need to create SAM interfaces in it. For example, if the Calculator
class is rewritten in Kotlin, its setAdder()
method can be written such that it directly takes a function as its argument, instead of an object that implements the Adder
interface.
class Calculator { var adder:(a:Int, b:Int)->Int = {a,b -> 0} // Default implementation // Setter is available by default fun add(a:Int, b:Int) { Log.d("CALCULATOR", "Sum is " + adder(a,b)) } }
While using the above class, you can set adder
to a function or a lambda expression using the =
operator. The following code shows you how:
val calculator = Calculator() calculator.adder = this::myCustomAdd // OR calculator.adder = {a,b -> a+b}
Conclusion
Android's APIs are largely written in Java, and many use SAM interfaces extensively. The same can be said of most third-party libraries too. By using the techniques you learned in this tutorial, you can work with them in your Kotlin code in a concise and easy-to-read way.
To learn more about Kotlin's Java-interoperability features, do refer to the official documentation. And do check out some of our other tutorials on Android app development!
- Android SDKAndroid O: Phone Number Verification With SMS Tokens
- Android ThingsAndroid Things: Creating a Cloud-Connected Doorman
- Android SDKCreate an Intelligent App With Google Cloud Speech and Natural Language APIs