Kotlin: Final Points
To round out this series on Kotlin and why I love it, this final post continues with a few additional elements of the language that stood out to me as extremely useful, coming from a Java background.
So far I have described the concise nature of the language, its increased safety both from a functional and conventional programming point of view, and some of the language features, such as extension and smart casting. This time I’m going to mention some items that fix nit picking things that annoy me about Java.
Equality Means it’s the Same
I did not come from a C or C++ background when learning Java for the first time, so the equality operator, ==
, was new to me. I was used to assignment with :=
and a single equals meaning an equality test. So getting used to double equals was the first hurdle.
Then I became aware that it does not really mean equals in terms of objects; it checks that references point to the same item. I hated that and still do. Kotlin has redefined the double equals to call the object’s equals()
method. The comparisons in the two println
statements in the following snippet are equivalent:
data class Person (val name: String, val age: Int)
fun main(args: Array<String>) {
val bob = Person("Bob", 25)
val bob2 = Person("Bob", 25)
println(bob == bob2)
println(bob.equals(bob2))
}
If you still need object reference equivalence you can get it by using the triple equals sign, ===
. Extending the example above, the statement println(bob === bob2)
prints false
.
Note that in the above example, Kotlin will create the equals
method for us because we used the data
modifier in the declaration of Person
. It also creates some helpful associated methods; see kotlinlang.org for more details.
No Checked Exceptions
One of the reasons that I love the Spring Framework is that they made a conscious decision to avoid introducing any new checked exceptions to Java; there are enough of them there already! I hate checked exceptions and the amount of boilerplate code that they cause to be written inside applications.
All Spring defined exceptions are extended from java.lang.RuntimeException
and therefore do not require explicit handling in code. No need to add throws
clauses all the way up a method call stack.
Kotlin goes a little further, in that you no longer need to add throws clauses to functions; Kotlin does this for you under the hood. That does not excuse you from needing try/catch blocks in your code but at least your method signatures will no longer need to propagate long list of potential exceptions up the stack to the class where you need to deal with them.
Delegate by
There are times when implementing an interface that you want to rely on a delegate, rather than write your own implementation of all the methods on an interface. This might lead to a lot of boiler plate where each overridden method simply calls the delegate.
Kotlin offers delegation as a first class language construct. Consider an interface with a a few functions:
interface Something {
fun actionOne()
fun actionTwo()
fun actionThree()
}
There is a simple base implementation that will be used as a delegate as follows:
class BaseClass : Actions {
override fun actionOne() = println("Base class action one")
override fun actionTwo() = println("Base class action two")
}
Note the by
keyword in the following class definition. It indicates that the interface will be fulfilled by the object specified.
class WithDelegate(val delegate: Actions) : Actions by delegate {
override fun actionTwo() = println("Class with delegate action two")
}
Even though the WithDelegate
class does not implement both of the functions defined in the interface, it can still be instantiated.
fun main(args: Array<String>) {
val base = BaseClass()
val withDel = WithDelegate(base)
withDel.actionOne()
withDel.actionTwo()
}
This can be useful to avoid a lot of code which simply passes calls along to the delegate object.
Multiple Classes per File
One of the things that I am less sure about in Kotlin is the ability to store functions and classes in an arbitrary fashion in the file system. It is no longer necessary for the file name to match the class name and therefore multiple classes can be stored in one file.
This may make the management of multiple small, related classes easier to manage, in particular when there are are many, small, simple classes. But it also might lead to some confusion for people coming from a more restrictive Java background.
I like that functions, an in particular extension functions do not need to sit inside classes. This has allowed Kotlin to avoid the use of the static modifier. Under the hood, extension functions are actually defined as static methods.
Imports work just like in Java, but if there is no class specified, how does this work? Consider a function myFunc
stored in a file MyFile
. You can import the function with import MyFileKt.myFunc;
– note the addition of the Kt
to the file name. Kotlin allows you to specify an explicit name if necessary using the @JvmName
annotation.
Operator Overloading
Kotlin allows you to create your own definition for symbolic operators that use the common mathematical symbols. To do so, you define a function for each operator that you require and use the operator
modifier in the definition. The following table shows the function names to use to override various symbols.
| Symbol | Function name |
|--------|---------------|
| - | plus |
| - | minus |
| * | times |
| / | div |
It is also possible to create unary, comparison, index access operators and some others. See operating overloading on kotlinlang.org for more details.
Getting Started with Kotlin
I’d recommend Kotlin in Action for anyone that is interested in learning more about this new, exciting language. The authors, Dimitry Jemerov and Svetlana Isakova have been involved with the development Kotlin from the start.
Another useful resource is try.kotlinlang.org, where you can try out the language with the online portal that includes a Kotlin compiler. Complete the training koans to learn about all the new features of the language.