Using Kotlin for Android development has grown in popularity over the last year. Even to those who are not currently using it, the value proposition of the language immediately resonates, adding simplicity and power to existing paradigms of Java development. Learn from Jake Wharton in his Øredev talk about how Kotlin advances the language and design patterns for Android development to solve larger problems, allowing you to clean up ceremony APIs and tedious code. Leverage the great power of extension function expressions to heal your boilerplate code!
Why Advance the Language? (0:00)
Alright, everyone. Welcome. We are going to be talking about using the Kotlin language for advancing development on Android – advancing how we develop apps. To start off, we need to figure out why do we actually need to move Android development forward? Why do we need something that’s advancing development?
A classical reason that a lot of people make is that we’re stuck in this Java 6-ish, almost Java 7. It’s been called a wasteland.
javax.time is a library that came in Java 8, JSR310. This means we don’t have this on Android, so we’re stuck with the old date APIs and calendar APIs, which are fraught with problems.
There’re no streams, another Java 8 feature. We don’t have new language features such as lambdas, method references, and anonymous classes that don’t reference the outer class. Finally, we don’t have try-with-resources. Try-with-resources is in Java 7, and Android went to almost Java 7 in API 19, with a little bit more in 20, but not all the way. So only if you have a minimum SDK can you use that.
Java 6 is Not the Problem (1:24)
But as it turns out, even though these four things are really painful, these aren’t true. For javax.time, the guy that wrote the majority of the code for Java 8, for the JDK, actually back-ported the library to Java 6 in the form of ThreeTenBP, so you can use that.
For streams, there’s also a back port that takes it back to Java 6, or a popular Java library, RxJava that implements the same concept in a slightly different, and arguably, more powerful way.
There’s a tool called Retrolambda, which will actually take Java 8 bytecode and back-port these lambdas and method references back to Java 6.
Lastly, like I said, if your min SDK is 19, you can use try-with-resources, and otherwise Retrolambda will allow you to do that as well.
These arguments that people make about Java 6 being a problem have tools to solve. Granted, it would be nice if we could just Java 8 out of the box, but these work-arounds are fairly well battle-tested and certainly are available and work. So that one doesn’t really count. Nevertheless, there are two that I really want to focus on, which can’t be fixed in the same way.
Java Language Restrictions and Problems (2:35)
Restrictions in the Java language and the problems it presents and a language can’t be worked around. For example, we have this inability to add methods to types to classes or interfaces that we didn’t write ourselves. Traditionally, these wind up in util classes, just littered all over your code base or slammed together in one util package. While this is a solution, it’s certainly not ideal.
The type system in Java, and especially in using it on Android, has problems with null, since it has no first-class representation of types that may be null or may not be null. Therefore, oftentimes the so-called “billion dollar mistake” of the null pointer exception ends up killing your app.
Next, Java is certainly not the most terse language. That’s not necessarily a bad thing by itself, but there’re many common idioms that just end up being overly verbose. This can potentially lead to errors and bugs in your code. On top of these, we have to deal with issues in the Android API.
Android API Design Problems (3:57)
Android is a big inheritance party; they love their inheritance, which works well for them, but it tends to push problems on us as application developers. Furthermore, the nullability problem comes back, and this is especially apparent in Android, where they want things to be efficient. null is used in a lot of places to represent the absence of a value, instead of something that would wrap it in a more high-level type, like an either type or optional.
Next, back to the verbosity, Android’s APIs themselves have a lot of ceremony. This also goes back to just wanting to have high performance. The designers end up writing these APIs that require you, the developer, to do a lot of things in order to provide efficiency, instead of abstracting it all the way.
So Java the language and Android APIs are really the two motivating factors for why something like a language like Kotlin could be appealing. It’s not to say that the Java 6 stuff isn’t a problem, because Kotlin addresses a lot of that stuff, but I don’t think it’s a good enough argument to make because of those alternate tools.
Introduction to Kotlin (5:19)
Kotlin is a language by the company JetBrains. They are developers of IDEs for tons of different languages, the Intelli J platform, which is the basis for Android Studio. On their website, they describe Kotlin as:
Statically typed programming language for the JVM, Android and the browser.
It targets the JVM and Android and Java 6 bytecodes. They want to add these new features in the language, while still supporting these markets of the Java 6 and up JVM, and, obviously, Android. They focus heavily on inter-op with Java, which I will discuss later below.
Kotlin Syntax Crash Course (6:07)
The website and the tutorials are great for going through the syntax, but I will provide a quick introduction of it before explaining why it’s good for Android. To start with, this is what a method looks like.
fun sum(a: Int, b: Int): Int {
return a + b
}
We have a “fun” declaration, which stands for function. The name of the method, and the first thing to note that’s significantly different than Java, is that the order of the argument name and the argument type is reversed – the type comes after the name. The return type is specified on the end of the method. There are no semicolons.
Another interesting fact is that because this function has a single expression inside of it, we can actually get rid of the curly braces and the return and just make the method equivalent to the expression.
fun sum(a: Int, b: Int) = a + b
We’ll see this shortened syntax comes in really handy later on. Here’s another example, this would be what a main method looks like, if you’re writing just a normal Java application:
fun main(args: Array<String>) {
println("Args: $args")
}
The array syntax for a type is a little bit different. It’s treated more like a generic. The bytecode that gets emitted will use a string array, but in your code you get to treat it like it’s just a generic array. There is also string interpolation; we can write strings and reference variables inside of them and have it be automatically replaced.
Finally, just a look at some variables:
val name = "Jake"
val people: List<String> = ArrayList()
Here I am assigning a string to a variable called “name,” and there’s no type here. It’s actually going to infer the type because it can only be a string. It’s prefixed with “val.” “Val” is a value, which is an immutable type and can’t be changed. If it were something that was mutable, and we wanted to change it, we would use “var” for variable.
The : List<String>
is what a type looks like on a field, it comes after the name, just like in a method. Finally, when we call constructors, we actually don’t have to use the “new” key word. It’s the exact same syntax for the rest of it, but just dropping the “new.”
Kotlin Language Features (8:57)
Let’s move on into the features of the language, and how they’re going to help us in building our Android apps. I pointed out that these util classes are almost an anti-pattern, and they end up growing out of control in your application…
Extension functions (9:18)
Kotlin has this concept called extension functions. It’s not unique to the Kotlin language, but it ends up working out in a slightly different way than we see in other languages. If we wanted to add a method to date in just pure Java, we would write a date utils class or dates class, and put a static method on it. That just takes in an instance and does something, potentially returning a value.
static boolean isTuesday(Date date) {
return date.getDay() == 2;
}
boolean tuesday = DateUtils.isTuesday(date);
Here I’m adding the ever-useful “isTuesday” method to our date utils and then we would just call it as a traditional static method. Before I show the Kotlin syntax, I want to contrast this with C#. This is what an extension method would look like in C# on its version of the date class, DateTime
:
static boolean IsTuesday(this DateTime date)
{
return date.DayOfWeek == DayOfWeek.Tuesday;
}
Here we get our date instance and we can call this method, and this actually works anywhere in your entire project in .NET. As long as there’s an extension method declared somewhere, you can use that on any DateTime in the entire project. I’ll explain why that’s an interesting distinction shortly. Here it is in Kotlin:
fun Date.isTuesday(): Boolean {
return getDay() == 2
}
val tuesday = date.isTuesday();
In Kotlin, we prefix the method name with the type that we want to add the method to. Instead of just isTuesday
, we’re now Date.IsTuesday
. Then you can see our return type at the end. We end up calling “getDay” and so this extension function is acting as though it was on the type so we don’t have any instance to call. We just call as if we were already a method on that class. We would call that any other method on Date.
One of the nice language features of Kotlin is that is actually automatically will convert types that have getters and setters to have synthetic properties. So I can replace getDay()
with just day
, referring to this day property which exists. It looks similar to a field, but it is a property – the concept of a getter and setter merged together.
I pointed out earlier that single expression methods can be put into a more terse syntax, so we can actually just move that up into the top and get rid of the inferred return type:
fun Date.isTuesday() = day == 2
Now we have this nice one-line extension method that we can use on date.
Unlike C#, extension methods have to be explicitly imported if they’re not in the same package. We would have to be very explicit about where this extension function is coming from if it’s not in the same file by writing: import com.example.utils.isTuesday
This is very similar to how Java already works with its static method imports. We have to be very explicit about the method that we’re calling and this allows it to be unambiguous about the source of that method. Because if I look at this, I know there is no “isTuesday” method on date as a Java developer, but the explicitness of the import tells me that it’s coming from this com example util package, whereas in C#, you have no idea where that extension method is coming from. It might be from a library, your source, or another place, and you have no way of knowing statically without actually going into the IDE and allowing it to figure it out.
Of course, you can command B and step into this method, it behaves just like it was a method on the date type. This generates the exact same byte code as it would have if we wrote it in Java. Also, because it focuses on inter-op, you can call it from Java using an automatically generated class.
Null in the type system (18:22)
I talked about nullability being a problem. Kotlin actually represents null in its type system. For the get string or null method, I’m returning a String?
to indicate that this might return a null value. Then down in the get string method, I’m just calling the or null and using this double exclamation mark syntax. This basically says, “I know this is not going to be null, so turn it into just a normal string.” This will actually emit a check where if it is null, it will throw an exception.
But in the consuming code, the code that’s calling this method, the type will be a string question mark that will be propagated that will be propagated down to the calling code. That means that you can never de-reference that field without first doing a null check or providing a default. So it virtually eliminates the ability for this to cause a null point exception in the code that’s consuming it.
Function expressions basics (19:30)
Elsewhere, function expressions are known as just lambdas or maybe closures. So here’s an example of the simplest version of a function expression: { it.toString() }
. It’s basically just a block of code that is calling the two-string method on this “it” variable. “it” is actually a built-in name. When you’re writing these function expressions, if there’s only one argument being passed in to this block of code, you can refer to it as “it” and it’s just a way to not have to define arguments.
But if we do need to define arguments, or there’s more than just one, this would be the syntax for doing that: { x, y -> x + y }
. We can create a piece of code, a function expression, which takes in two pieces of data and adds them together. If we want, we can specify explicit types.
{ x, y -> x + y }
val sum: (Int, Int) -> Int = { x, y -> x + y }
val sum = { x: Int, y: Int -> x + y }
Then, to store these in fields, so now you can see that the first second example, the type is declared on the field itself. This means that the function expression doesn’t need to include any type information. Then down at the bottom, the type information is included in the function expression itself, so we don’t need to include it in the variable declaration.
Lastly, in the last one, the return type is inferred. It knows that two integers are going in, and it knows that it’s adding them together, and the only thing that can come out is an integer so you don’t have to be explicit about it.
Higher-order functions (21:59)
This is just a really fancy term for functions that accept a function, or functions that return a function. Here’s an example:
func apply(one: Int, two: Int, func: (Int, Int) -> Int): Int {
return func(one, two)
}
val sum = apply(1, 2, { x, y -> x + y })
val difference = apply(1, 2, { x, y -> x - y })
Here we define a function, which is going to take in two integers. Then as its third argument, it’s actually going to take in a function. That’s the same syntax that we saw previously, where we’re defining a function that takes two integers and returns an integer.
Then, inside the method body, it’s just going to call that function with the two parameters. Underneath it is how we would use it to create a sum or a difference. We’re applying this block of code onto these two numbers. This goes back to the point where we’re taking this code that knows how to add or subtract, and we’re turning that into data that we can pass into this apply method. Then it can take care of running it when it has the appropriate context to run it.
Kotlin actually gives you a handy way of turning this into a slightly nicer syntax. If the last argument of a method is an expression, you don’t have to have the parenthesis all the way at the end, you can just have it after all the initial arguments.
val sum = apply(1, 2) { x, y -> x + y }
val difference = apply(1, 2) { x, y -> x - y }
This technique is going to allow us to create really nice DSLs and APIs later on. You can also write apply as an extension of Int.
Applications of higher-order functions (25:02)
fun <T> List<T>.filter(predicate: (T) -> Boolean): List<T> {
val newList = ArrayList<T>()
for (item in this) {
if (predicate(item)) {
newList.add(item)
}
}
return newList
}
val names = listOf("Jake", "Jesse", "Matt", "Alec")
val jakes = names.filter { it == "Jake" }
A common operation is just having a list of things and needing to filter it based on some condition. Because we have extension methods and higher-order functions, we can write a method on lists that does this for us. It is implemented just as you’d expect, comparing each of the list items and returning those that return true.
Now if we have some list of data, in one line of code, in this nice, terse little function, we can now filter it to only pull out the things that we need. Thankfully, this function is actually built into the Kotlin standard library so we don’t actually have to extend lists.
A large majority of the standard library of Kotlin is just augmenting these Java types that already exist with these higher-level methods that provide common operations on them.
Check out this application that doesn’t exist in the Kotlin standard library:
fun Any.lock(func: () -> Unit) {
synchronized (this) {
func()
}
}
Instead of doing synchronized blocks, we could use an expression to execute code in a synchronized block. So “Any” is Kotlin’s version of object. Every type is a sub-type of “Any”. We can add a method to it that just takes in a function and executes it inside a block that’s synchronized on that instance. If we have some lock, or some object that we need to lock on, we can put the code that executes inside that lock inside a function. We then pass that to this method that’s on every instance. This approach cleans up the calling code, and makes it a bit clearer.
Easy and Clear Locking Example (27:29)
Another pretty cool application of this is that a common problem with doing locking is that oftentimes, you’ll have code that forgets to do a lock before it does some operation. We can write a class that actually only allows you to access the type that you need to interact with if you have a lock on it.
data class Lock<T>(private val obj: T) {
public fun acquire(func: (T) -> Unit) {
synchronized (obj) {
func(obj)
}
}
}
val readerLock = Lock(JsonReader(stream))
// Later
readerLock.acquire {
println(it.readString())
}
In this case, we’re creating this JsonReader, which is on some stream. Suppose we’re accessing it from multiple threads for whatever reason, this will prevent us from accessing this instance unless we take this lock, which is going to take care of synchronizing for us.
So later on in the code, we call this acquire method, and it’s going to synchronize on that instance, and then pass it to the function that we gave it. And so in this case, I re-destroying from this JsonReader, and we can only do that if we have the lock. Now we’re not dealing with locks at all. We’re not dealing with the explicit synchronization and having a lock and the JsonReader. It’s impossible for us to access that JsonReadon without taking the lock.
Preventing Leaks with Kotlin (28:39)
Earlier on, I talked about the code as data, defining a value like so:
val notEmpty: (String) -> Boolean { !it.isEmpty() }
The way that Kotlin implements function expressions is the same way that you would do it in Java using classes. What’s nice is that it’s not actually creating a reference to the outer scope at all since there’s no class, avoiding a possible context leak.
All we care about is the data being passed in. It’s going to create a singleton instance that is static and has no reference to the instance at all. It’s basically impossible to leak a context with these function expressions. But at the very top here, we have two that are the same.
Extension Function Expressions by Example (30:29)
-
Extension Functions – Functions added to a type without modifying the original.
-
Function Expressions – Undeclared function bodies used as an expression (i.e., as data)
-
Higher-Order Functions – A function which takes a function or returns a function.
Extension function expressions are the combination of all three of these concepts into a really powerful way to make clean APIs. To demonstrate that, I’m going to take a databases API that and turn it into something much more terse that prevents us from writing invalid code.
db.beginTransaction();
try {
db.delete("users", "first_name = ?", new String[] { "Jake" });
db.setTransactionSuccessful();
} finally {
db.endTransaction();
}
These are the six lines of code that you would have to write if you wanted to execute a statement in a transaction. We start the transaction, we put it in a “try finally.” We execute our code, then we have to mark the transaction as successful. If that throws or it doesn’t throw in the “finally” block, we have to end the transaction.
This is the type of code that’s prone to bugs, to forgetfulness, to refactors. Anything could go wrong here, where you accidentally swap two things, and all of a sudden, you have a very subtle bug that’s hard to catch. Or maybe it just blows up at runtime.
Extension function expressions are going to allow us to fix that. We can now add a method to the database itself, which is going to encapsulate the boiler plate of that chunk of code.
fun SQLiteDatabase.inTransaction(func: (SQLiteDatabase) -> Unit) {
beginTransaction()
try {
func(this)
setTransactionSuccessful()
} finally {
endTransaction()
}
}
db.inTransaction {
it.db.delete("users", "first_name = ?", arrayOf("Jake"))
}
The starting the transaction, the doing the “try” block, and setting the transaction successful, and finally, ending it. So we take in a function, and we run that function inside the “try” block after we’ve done that little dance. In our consuming code, we just have this method that we call inTransaction
on and anything that we put inside that is going to execute inside a transaction. There’s almost no way for you to screw this up.
Channel your inner SQLiteDatabase (32:39)
Interesting thing here is, the way that we’ve implemented this, you still have to reference the database inside the piece of code. That could potentially be something that you mess up, if you have two databases, maybe you referenced the wrong one inside a transaction for the other one.
We have ways to fix this. One way we could do is we could actually take in a function whose arguments takes in the database that the transaction is on. We have to pass in this to the function, and then now instead of calling “db,” we can use that “it,” that first argument that’s passed to the expression.
This is still annoying. We’ve potentially prevented a bug, but now we have to specify “it” every single time you want to access this database. What’s interesting here is that the argument that’s coming in is a function. And we can change that function to, actually, itself be an extension function on “SQLiteDatabase.”
This is going to blow your mind, as it’s actually a crazy-powerful concept. The function that’s being passed in is now an extension function on that object by replacing func(this)
with this.func()
. Instead of passing in this into the function, that function actually exists on the “SQLiteDatabase” now. Because we don’t really need this
, we’ll just get rid of it.
The function behaves as if it was defined on SQLiteDatabase
. If we want to call this delete method, we don’t have to qualify it with anything because we are a SQLiteDatabase
. We can now get rid of that it
and so now every expression that you put inside this block is going to behave as if it was a private method inside SQLiteDatabase
. You don’t have to qualify it with anything; it will always execute on the right object.
This is fantastic. This is what we’ve turned our code into:
db.inTransaction {
it.db.delete("users", "first_name = ?", arrayOf("Jake"))
}
Avoiding extra garbage collection (34:43)
There’s still a huge problem for Android. The equivalent in Java code is actually creating a new instance of a function and passing it into our generated static method. This is unfortunate, because before we just had a bunch of lines of code that are executing in order, not really doing any allocation.
If we do this in Java, we have to allocate these tiny little function objects every single time we want to use these extension function expressions. This is not good, since it will end up causing a lot of GC.
Thankfully, there’s a way to get rid of it in Kotlin. Our function that we have here we just defined as an extension function. It takes in this function expression, and that’s the problem. That function expression has to turn into an anonymous class.
inline fun SQLiteDatabase.inTransaction(func: SQLiteDatabase.() -> Unit) { ... }
We can actually take this and turn it into an in-line function, we can tell the Kotlin compiler that instead of just calling it as a static method, I need you actually take my body, and replace it in the calling code. Although this generates a lot of bytecode, the generated Java class file ends up looking the exact same as if we were compiling this potentially error-prone Java code.
Rule of thumb: extension function expressions plus an in-line function equals the exact same equivalent as the Java code. Now we are equipped to clean up any API that we want; pick your worst API in Android, there is certainly no shortage. There’re a lot that are transaction-based, so we could re-use the same pattern for fragments or for shared preferences. This is a great way to add functionality to these types with no overhead in the actual generated code.
Anko by JetBrains (37:32)
The JetBrains people have taken this to a ridiculous extreme and created this library called Anko. The idea is that XML is very declarative and it’s actually perfect for UI because it’s hierarchical. UIs are inherently hierarchical when you’re building them up.
With these extension function expressions, we can now represent that in code in that same hierarchical way, while still having the ability to reference all of the methods and all of the refactoring tools, and all of this static analysis of Java code itself for representing UIs.
verticalLayout {
padding = dip(30)
editText {
hint = "Name"
textSize = 24f
}
editText {
hint = "Password"
textSize = 24f
}
button("Login") {
textSize = 26f
}
}
These are just a bunch of extension function expressions. These are going to create instances of these classes, add them to their parents, set the right properties. Instead of the concepts that we use in XML, including layouts, you can actually just call a method that returns this partial layout and compose these things together.
It’s certainly an interesting concept. It’s definitely not for everyone. They also have a custom preview plugin. One of the nice things about XML is you get to pre-render the view and see how it will most likely look on a device. They’ve written one for this Java code so it will statically parse the Kotlin and do that rendering as well.
It’s a great example of taking something that annoyed them, which is the XML and the general pain of having these two separate worlds of Java and XML, and resources, combine them into a single source, which is just the Kotlin source file. This ends up being faster than XML because you eliminate the overhead of parsing the XML, and you’ll eliminate the reflection that’s incurred when you look up the classes that are defined in XML.
So although it’s not for everyone, it’s a novel solution to a problem that they perceived.
Conclusion (39:37)
I really just wanted to give an introduction to the concepts that are most useful for fixing the problems of Android. There’s a ton of other things in the Kotlin language that are just general improvements, but that’s for any Java code.
There’s these very specific things which allow you to take the problems of Android and potentially remedy them. The Kotlin website is absolutely fantastic with a ton of resources. There’s an interactive editor, where you can actually create and run Kotlin code in your browser.
They also have a series of interactive tutorials that you can step through that will teach you the syntax in that same editor.
The language just arrived at its 1.0 beta stage, so for those holding off because of that reason will soon be able to jump in. I encourage you all to go out and experiment with Kotlin.
Receive news and updates from Realm straight to your inbox