Support

Language

Kotlin: A New Hope in a Java 6 Wasteland

This talk was delivered live in August 2015 at Droidcon NYC. The video was transcribed by Realm and is published here with the permission of the conference organizers.

Java 8 added lambdas, streams, and many other language improvements. Java 9 is coming in September 2016, but with over half of Android devices stuck using Java 6, will we ever get to use a modern language?

In this talk from Droidcon NYC, Michael Pardo introduces Kotlin: a statically typed JVM language backed by JetBrains. With features like lambdas, class extensions, and null-safety, it aims to be concise, expressive, and highly interoperable — a powerful addition to your Android tool belt.

Hi! I’m Michael Pardo, and I’m here to show you Kotlin and how it’s going to make you a happier and more productive Android programmer. Kotlin is a statically typed language targeting the JVM. It’s backed by and created by JetBrains, the company that brings you Android Studio and IntelliJ.

There are a few core tenants that Kotlin lives by. It aims to be:

Concise, reducing the amount of code it takes for you to achieve something;

Expressive, making your code more readable and understandable;

Safe, removing the possibility for you to create errors;

Versatile, targets the JVM and JavaScript you can run it in many places;

Interoperable, what that means is you can call Java code from Kotlin and Kotlin code from Java, and they aim to be 100% interoperable.

Let’s take a look at the history of Java and Android and their relationship. In 2006, Java 6 was released. A couple of years later, Android 1 was released as an alpha, and four years ago, Java 7 was released. Android followed up with Java 7 support two years after that, and last year, Java 8 was released.

When do you think we’ll get Java 8? It might be a while, if we do get it at all. Even then, if we do get Java 8 support in the next couple of years, it’s going to be a while longer before everyone can adopt it. Android adoption is fairly fragmented now, and Java 7 is only supported for API 19 and up. Your userbase for Java 7 apps is just over half, which isn’t great. Even if we had Java 8 right now, with 100% compatibility on all devices, the language itself has some problems.

Null references, coined by its inventor as a billion dollar mistake: You can code very defensively, but null pointer exceptions sneak into everyone’s code just because Java’s type system is not safe.

Raw types: We’ve been stuck with raw types in order to stay backwards compatible, and we should all know how to avoid raw types, or that we should, but they’re included in the language as kind of misleading and unsafe.

Covariant arrays: You can create a string array, and an object array and then assign that string array to that object array. It compiles fine, but once you try to assign a number to that array of strings in the runtime, it’s going to throw an exception.

Java 8 has higher-order functions but they’re implemented with SAM types. SAM is single abstract method, every function type needs an interface correlated to it, and if you want to create a lambda function that doesn’t exist or doesn’t have a function type, you need to create that function type as an interface.

Wildcards in generics, anything but non-trivial generics can get kind of out of control and hard to read and write and understand, and checked exceptions. These are like exceptions in the method header, or the signature, rather. They’re good-intentioned, but a lot of time what you’ll see is a code base littered with try/catch blocks and ignored exceptions.

For pretty much all the issues that we just went over, Kotlin just removes those features. It also adds great things like:

Lambdas

Data classes

Function literals & inline functions

Extension functions

Null safety

Smart casts

String templates

Properties

Primary constructors

Class delegation

Type inference

Singletons

Declaration-site variance

Range expressions

We’re going to cover most of these in this post. Kotlin can keep moving forward with the JVM ecosystem because it doesn’t have any restrictions. It compiles right down to JVM bytecode, which means no overhead. It looks to the JVM just like any other JVM language. In fact, if you use the Kotlin plugin for IntelliJ or Android Studio (which you should), there is a bytecode viewer that displays the generated JVM bytecode for each method in headers.

Let’s have a look at some basic syntax. Here’s the simplest form of a Java Hello World:

funmain(args:Array<String>):Unit{println("Hello, World!")}

It’s just a function and a print statement. There’s no package declaration. There are no imports required. This is all that’s needed to start up a Kotlin Hello World. Functions are declared with a fun keyword, followed by the function name, and then the arguments in parentheses just like Java.

However, in Kotlin, you put the argument name first, followed by the type, separated by a colon. The function’s return type is last, instead of first like in Java. If a function doesn’t return any useful type, it returns a unit as the type, and we can infer the unit in most cases and simply omit that return type. Then we print Hello World. This is a standard function in the Kotlin standard library and that calls into Java’s system.out.println.

Get more development news like this

Comments

funmain(args:Array<String>){println("Hello, World!")}

Next we’re going to extract the “World” portion of that string into a variable. The var keyword followed by the variable name, and then Kotlin also has string interpolations we can include that variable right in the string without having to concatenate it. The way we do that is just by including the variable name, prepended with a dollar sign.

funmain(args:Array<String>){varname="World"println("Hello, $name!")}

Next, let’s expand this to check and see if the arguments that we passed in have any strings. If this string array is not empty, we’ll take the first string and assign it to name. We also have the val keyword, and analogous to a final variable in Java — a constant.

And we run into a problem trying to compile this because we cannot reassign a value to this constant. One way to solve that is using an inline if-else block. A lot of blocks in Kotlin can return a value, going through the if statement and returning arg[0] if it’s not empty. Else, it will return “World”, and that assigns that right into the value we’ve named a name. This is a conditional assignment block:

Let’s look at classes. We have a class defined using the class keyword just like in Java, followed by the class name. Kotlin has primary constructors, we can put a constructor right in the class header. In that constructor we can define our parameters. We can also define them as properties by declaring them as a var or a val.

classPerson(varname:String)

Using the earlier example, we can take our name assignment logic and remove it, and replace it with an instance of the person class. When you create an instance in Kotlin, you don’t have to use the new keyword. You just create a new instance by referencing the type.

In Kotlin you can have default arguments, the default value for this argument is Language.EN, and then we can omit that value anywhere we create an instance of the class. We can make this example a little more object-oriented and add a function called greet to the person class, and that just takes the language greeting and appends the name into a string.

We can create a list of person using the listOf method, a standard library function. We can iterate that list of person using the for-in syntax.

for(personinpeople){person.greet()}

We can then call person.greet() on each iteration, or better yet, we can call an extension function off this person collection and pass on a lambda for iteration. We can reduce it down to its simplest form, removing the explicitly named value and using the provided name value which is it.

people.forEach{it.greet()}

We can move that up into the list of return value and eliminate that value people we declared. We can do a little better and create subclasses for each language, first we have to make the person class open which means it’s non-final. Classes by default are final in Kotlin.

Let’s create two classes and pass in a default language for each, and we can replace those declaration, instance declarations in the list with their respective subclasses. Below is our full extended Hello World, showing off a lot of Kotlin’s features.

You’ve probably seen type inference in other languages. In Java, we define the type, then the name, and then the value. In Kotlin, it’s a little backwards. You define the name, then the type, and then assign the value. In most cases, though, you don’t need to define the type. A string literal is good enough to infer a string type. The same goes for characters and integers and longs, floats, doubles, Booleans.

This also applies to any other type, as long as Kotlin can infer it. Usually, if it’s local, then you don’t need to specify the type when you declare a value or variable. In cases where you can’t infer, you have to define the variable with the full syntax, which is with a colon and a type, and then equal sign.

One of Kotlin’s most powerful features is null-safety. we’ll go through an example.

Stringa=null;System.out.println(a.length());

Here in Java, we have a string. We’ve assigned null to it. As soon as we go to print that string, though, we run into a problem at runtime, and that’s going to be a null point error exception because we’re trying to access a null value. Here is the same code in Kotlin, we declare a null string, but before we can get it even further, we encounter an error with the compiler.

vala:String=null

The problem is we’re trying to assign a null value to a non-null type. In the type system, there’s null types and non-null types, and the type system recognizes that this is a non-null type and prevents compilation to make it nullable. To make any type nullable, we add this question mark to the end of it and that means it can be assigned with a null.

vala:String?=nullprintln(a.length())

Now we can continue, now that we’ve fixed that error, and just like in the Java example, we print the length of the string, but, we run into the same problem as in Java. This string could be null. However, in this case, we run into it at compile time instead of runtime thanks to the type system.

The compiler stops here, and to fix it, we need to prepend the call, or append the variable with a question mark to make it safe. This essentially checks to see if the value is null before accessing it.

vala:String?=nullprintln(a?.length())

If the value is null, it returns null. If it’s not null then it returns the actual value. Since this value is null, null is passed to the print method and we print out null.

Above is something you might see in Java. To access a value at a ternary operator, we check that the value is not null and then we can finish the assignment using the value. If the lefthand side passed the null check, we can access it. If the righthand side was null, we need to do something else. In Kotlin, you can do the exact same thing.

varlength=if(a!=null)a.length()else-1

If a is not equal to null, then you can access it, otherwise, return your default value. But there is a shortcut for this called the elvis operator.

varlength=a?.length()?:-1

We do an inline null check using the question mark, and if you remember, it’ll return null if a is null. If anything on the lefthand side of the elvis operator is null, it will use the righthand side. Otherwise, it will continue with the evaluation.

Kotlin has a feature called smart casting. If a local object passes a type check, you can access it as that type without explicitly casting it. Here’s an example:

if(xisString){print(x.length())}

We check to see if x is a string, and if it is we print the length. To do that we use the is keyword. It’s similar to instanceOf in Java, and since it passes the check, we can treat it like a string anywhere after the check.

if(x!isString){return}print(x.size())

It also works in reverse, here we check if it’s not a string, and if it’s not, we return out of the function. However, if it is a string, everywhere after that we can assume it was a string and we don’t have to cast it. Casting anywhere after the type check even includes inline logic.

if(x!isString||x.size()==0){return}

Here we’re checking if it’s not a string and then we use the or operator, if the lefthand side was false. Since it’s a string, we can access it as a string and it gets casted by Kotlin.
Same goes for when statements. when statement are like an enhanced switch statement:

A lot of languages have string templates or string interpolation. This is how you might do it if you’re used to Java.

valapples=4println("I have "+apples+" apples.")

You would concatenate this apples value in the middle of the string. In a more Kotlin-like fashion, if you’re using string interpolation, you’d just move that inside of the string and prefix it with a dollar sign.

In Java, you might do something in the middle of the string where concatenating an expression of apples plus bananas but we can move that into the string and interpolate it and the only thing we have to do there is wrap it with curly braces after we use the dollar sign prefix.

You might have seen these in other languages, too. You’ll notice that a lot of these features have come in from other languages. Here’s an expression saying that if i is greater than or equal to one and less than or equal to 10, then we print it. We check if it’s in range of 1 to 10.

if(1<=i&&i<=10){println(i)}

Instead we can create use a construct that’s already created for us called an intRange. We pass in the range of 1 to 10, and then there’s a contains function that we can call to see if it’s in that range, and if it is, we print i.

if(IntRange(1,10).contains(i)){println(i)}

This can be reworded using extension functions, 1.rangeTo creates an intRange of 1 to 10, and we can call contains on that.

Preferably, you want to use this operator:

if(iin1..10){...}

This aliases over rangeTo. Because of the null type system, we can be smart about this. There are no primitives in Kotlin. There’s int, which is integer or a primitive int, and depending on whether it’s null or not, the type system knows whether to use a primitive int or an integer, optimizing it for your case. It’ll automatically box any primitive that exists in Java, as long as you’re using the right type.

We can also iterate through ranges because they are iterate-able. You can use steps in your ranges:

for(iin1..4step2){...}

You can reverse iterate, or you can reverse arrange and iterate with steps.

for(iin4downTo1step2){...}

You can basically combine a lot of these functions to make range iteration as expressive as you want. You can iterate over pretty much any data type. You can create ranges for any data type including strings and your own types, as long as there’s a logical way for you to iterate and you can implement it. The step are internally defined as numbers.

First, we need to declare a function interface that takes type T and returns type R. Our method iterates over a collection using your supplied function and creates a new array lists, adding items that pass your predicate, and then we call the filter function. Finally, the anonymous implementation:

Above is pretty much what it looks like in Kotlin, or at least our version of it, and then the call looks like so:

filter(numbers,{value->value%2==0})

You’ll notice that there’s no interface defined here, and that’s because Kotlin has function types, instead of using an interface, we specify a type that is a function type that takes in a type T, and returns a type Boolean, and then we can invoke the function just like any other function. We pass an anonymous function which is a lambda that matches the function type that we defined in our filter function. We filter passing in a list and a function. Also, if the last argument is a function type, we can declare it outside of the argument list.

Since function type can be inferred from the function signature, it knows it only has one argument we can remove it and it’ll name it to the pre-defined name of it, like this:

Inline functions go hand-in-hand with higher-order functions. In certain cases, usually when you’re using generic types, you can add the inline keyword to the function. During compilation, it will take the lambda that you provide to that function, and it will move it inline into your code.

There are some problems with this function though. We can’t check the type because generic types are erased. Even if we could check for that type, we’ll get an unchecked cast warning.

The solution is to take our function and add this reified keyword to the type. Because that function gets compiled down to inline code, we can now check against the generic type to see if it’s an instance of that, clearing our cast warning:

We remove our argument and we need to change that code to use this, since we’re calling the function on the integer itself now. This is how we use it, we can call the function directly on the integer:

16.isLollipopOrGreater()

It can be any integer, literal or wrapped. You can also do this on final classes. Because it doesn’t actually add any code to the class, you can’t modify the class after what. Instead it creates a static method that looks like the original one, but has this syntax sugar that looks like it’s an actual function of that class. These methods can be defined anywhere.

Kotlin makes use of extension functions in Java collections, so here’s an example of taking a collection, and doing some operations on it.

We take the list of customers, map, filter, and sort it. The nesting made it messy and hard to read. Extension functions come in handy in the standard library as they can use Java collections and substantially clean up code:

You just define class members as variables, and these are public by default, and the encapsulation is taking care of by the compiler, so it’s encapsulating Kotlin and it’ll generate the getters and setters in Java.

Kotlin classes can have multiple constructors just like in Java, but you can also have a primary constructor. Below is our class from the last example, and now we’ve added a primary constructor, which just is a constructor that goes in the class header.

We can immediately use those variables to assign to our properties of this class, or we can move that into an initialization block:

Better yet, we can just define them as properties in the primary constructor, and we can format it nicely like so:

classCustomer(varfirstName:String,varlastName:String,varemail:String)

Now we have a class that takes in all of those arguments and their properties, with getters and setters. If we change those properties to values using the val keyword, the class becomes immutable, which makes it ideal for functional programming of the type we saw earlier along with concurrency.

Kotlin removes the concept of static in objects and methods. To help with inter-operability we have companion objects. Normally you’d see something like an activity with a concept string called tag and a static method to start the activity, creates an intent and you can access the tag statically, and you can access the method statically.

We have our companion object, which is kind of like a singleton for this class. Inside that we have a string constant for tag, and the start function, and then we access it the same way as we did in Java.

We have my special implementation of lists which takes a list of the same type in the constructor. We can store that list internally, delegate to it, and then it passes the list methods onto the delegate when we want to. Here’s the same implementation in Kotlin:

classMyList<E>(list:List<E>):List<E>bylist

Using the by keyword, we implement this list of E, and we take in a list of the same type in the constructor.

You can’t assign a list of string to a list of object, because they don’t inherit each other. If you try to compile this in Java, you’ll get an incompatible types error. To fix this we use use-site variance in Java. We define our list of objects as a list whose type extends object, and now we can do that assignment. For that reason, you’ll see stuff like this in Java’s collections:

It has declaration-site variance, you’ll have to split out your collections. This interface is a list that takes in type, something that extends type E, and returns type E. Then we have a mutable list, which takes in type E, and extends the list type E.

Now we have an immutable and mutable type of collection, and the variance is much easier to understand as it’s explicitly declared. That means that you can assign a list of strings to a list of anything, even if it’s an array list or a mutable list, as long as it makes sense and the compiler allows it.

Here we have an enum of coin that we created, and each coin has an integer cent value. And then we have a purse class, which has a float value for the amount of money it contains in dollars, and in this class, we create a function called plusAssign. plusAssign is a reserved function that the compiler will pick up on, and it will call this function whenever you use the += operator.

We create a purse instance, and we can add coins to it now, and that will modify the internal value of amount and there’s operator overloading for pretty much everything.

Adding to your Kotlin to your project is pretty easy as now you can just have it mixed in with Java classes. You’ll need to add the gradle plugin to your build script, apply the Kotlin Android plugin, add your source directories, and include the standard library. You should also install the Kotlin plugin to get language support in the IDE.

Resources to Get Started

Michael: There are some performance considerations to make with certain features, such as annotation processing being slow. Overall I’d highly recommend it though.

Q: Does it work with Unit Tests?

Michael: Yes, we use Robolectric, and plain old user tests, and we use Mockito with some caveats. Nonetheless, Kotlin’s interoperability allowed us to switch to it over time as our codebase grew.

Q: Can you still debug the whole way through?

Michael: It’s just JVM bytecode, and it’s integrated nicely with IntelliJ because JetBrain wrote both of them. We previously used Retrolambda, but the bytecode that it generates is not necessarily the source code that you wrote, leading to a breakpoint mess.

Q: What about Jack and Jill?

Michael: Jack and Jill won’t work with Kotlin because it goes from source right to text, and Kotlin still has to go to that bytecode step.

Q: Does Kotlin have a problem with reflection?

Michael: The only problem with reflection is that it adds another library that is about as big as the standard runtime. However, you can always drop down to Java objects for optimized reflection.