Learning Kotlin: Object Expressions and SAM Conversions

Note

For the 11th post, we get something new to an old C# person—Object Expressions! Object Expressions are very similar to Anonymous Classes in Java, where you can declare a class inline rather than entirely separately in its own file.

In the first Koan, we need to implement Comparator inline:

fun task10(): List<Int> {
    val arrayList = arrayListOf(1, 5, 2)
    Collections.sort(arrayList, object : Comparator<Int> {
        override fun compare(o1: Int, o2: Int): Int {
            return o2 - o1
        }
    })
    return arrayList
}

You can see on line 21, we define the new object with the object keyword and then use : Comparator to state it implements that interface. The Comparator has a single function which needs to be implemented, done on line 22.

The second Koan takes this further: if there is a single method in an abstract class or interface, we can use SAM (Single Abstract Method) to avoid needing the class at all—just implement the single function. To achieve this with the Koan, we use an anonymous function that handles the compare function of the Comparator:

fun task11(): List<Int> {
    val arrayList = arrayListOf(1, 5, 2)
    Collections.sort(arrayList) { x, y -> y - x }
    return arrayList
}

And lastly, if we look back at our previous post, we can simplify it further using the extension method:

fun task12(): List<Int> {
    return arrayListOf(1, 5, 2).sortedDescending()
}

These Koans have given me more thoughts about the language than probably any previous ones:

  1. Why do classes which implement an interface use override? In C#, when you implement an interface, you don’t need to state you’re not overriding the functions (see this example). In C#, you only specify override when inheriting from a function and actually overriding it. The reason? In Kotlin, an interface is closer to an abstract class than in C#—to the point it can have functionality (yes, interfaces can contain functions and logic!).

  2. So why does Kotlin have interfaces and abstract classes? The key difference is that an abstract class can have state, while an interface must be stateless.

  3. Why bother with SAM? As I worked on the Koan, I was delighted by the SAM syntax—then I wondered why I needed it at all? Why does Collections.sort take a class as the second parameter instead of just a function (since that’s all that’s needed)? Both C# and Kotlin support passing functions—but Java doesn’t! In Java, you must use Callable (a class) to pass functions.