When I began to write this article, a few weeks ago, I talked about the amount of new frameworks, architectures and languages that come out every day, and about the need to separate the chaff from the wheat before investing our time in trying to assimilate new technologies.
I had crushed it, but on 17 May 2017, barely a week ago, Google announced that it was adopting Kotlin as a high-level language for Android development. In other words, the world has now been divided into those that shout ‘I knew it’ and those that whisper ‘but what does it mean?’.
I know that I’m asking you for an act of faith, but I can’t help but point out that at Paradigma we were on the side of the visionaries. So, without any more introduction, we’ll start with the most basic stuff…
What is Kotlin?
Kotlin is a static programming language for JVM, Android, browsers and soon LLVM, 100% interoperable with Java, created by Jetbrains, the company responsible for, among others, Intellij Idea.
That being said, it seems like just another JVM language of which there are many at this point, right? In a certain way, this is true, but the devil is in the details...
Why Kotlin?
Who hasn’t ever done something in JavaScript? Come on, raise your hand if you have ever flirted with the idea of putting Full Stack Developer on your CV. And who hasn’t lost their temper getting confused between double or triple equalities, or converting some random value in truthy and going crazy while debugging their if conditions?
I don’t need to see you to know that almost all of your hands are still up (and whoever lowered them did so out of shame, you’re not fooling me!).
JavaScript can be many things, but if I had to define it in one word, it would be confusing. And, before the front people get angry with me, how do you explain that the most recommended book about the language is this one?
Special mention to the subtitle: unearthing the excellence in JavaScript. Unearthing. This is no joke.
The crux of the issue is that a programming language, like any other tool, has to make what’s right simple and what’s wrong more complicated. Like Mr. Crockford, and five hundred other videos on YouTube, show us, it seems that JavaScript sometimes insists on doing the opposite. And that’s how it goes.
But you shouldn’t laugh at java-ists, since we also have our ten commandments for how to program without tying a rope around our necks…
Our friend Joshua Bloch distinguished himself here with an indispensable book. Whoever has been programming in Java for ten or fifteen years and hasn’t read it should not think that they have nothing to learn from it, quite the contrary. Now is when they will be able to get the most out of it, every time that they see a solution to one of those blunders that we all have on our conscience.
What does it mean for a language that books as essential as these come out? That something is rotten in the state of Denmark. Honestly, I think Java is the best choice for software development today, an unsurpassed ecosystem and a language that has picked up many good ideas and allowed us to get very far. But, let's face it, the passing of time is not without consequence and now we see clearly that, besides good ideas, there are also clearly outdated design decisions.
We have two options: we learn to program brilliantly, investing time and extra effort in it, and avoid picking up our tool on the side that cuts, or we look for a better tool. Because it is not enough that you do things well, you have to trust that the rest of your team does too... and that your versions of ‘well’ match.
It’s possible, clearly, and many of us do it day after day. (At Paradigma, of course, we all do it, what the hell!) However, why put in more effort than necessary when there are better and, dare I say, more fun options?
But we already have other languages…
That’s true, and without even leaving the JVM we could talk about Jython, Groovy, Scala, Clojure, Ceylon, Xtend… Or decide that we don’t care about limiting ourselves to Microsoft and dedicate ourselves to C#, which emerged from the shadow of Java to become a very good language with a platform with very few possibilities, sadly.
[caption id="" align="aligncenter" width="790"]
The first great debate on languages of History[/caption]
But Jetbrains were very clever when they decided that maintaining the code of their products was too cumbersome and that they needed a better language, as they saw a gap in the market that no one was covering. There was room for an alternative as long as it was:
- A language for JVM. Because it is the best runtime in the market by far, unrivalled for large servers but capable of running software on a normal PC.
- Easy to learn for the java-ists, which is where options like Scala or Clojure fail. It was always the intention to make a practical, non-academic language so that any of the 9,000,000 Java developers in the world could catch on to it practically, understand it without much difficulty, and be productive quickly.
- 100% compatible with Java, in such a way that in the same software parts made in Java can be mixed with parts made in Kotlin in a transparent way. In other words, you don’t have to throw out everything you’ve done and start from zero. You can make a new class in Kotlin, or a test, and it will work perfectly alongside the rest of the project. Another consequence of this is that you can use any Java library within Kotlin. Or make a Kotlin library and use it within Java. Make note of this, which might seem incidental but, in my opinion, is amazing. So much so that I’d put this whole paragraph in bold if it wouldn’t be so ugly.
- Strongly typed. Because a test that tells you that your code is bad is good, but it’s better if the compiler can tell you. For this reason Intellij Idea can help you to convert code, auto-complete it, analyse it… And here the Groovy people start to cry.
- Proved in production. Experiments are good for the typical toy project, but for something to use for work it’s better to be sure that it isn’t going to give more headaches than necessary. Jetbrains have been developing the language for six years, and almost from the beginning it has been used in the development of their products. It works for them. And for Trello, and for Basecamp, and so many others.
- Compatible with Android. In my opinion, this was the coup de grace. With more than 80% of the smartphones market, we Android developers have found ourselves tethered to a kind of Java 7 that, in view of the fortune that has been spent on lawyers by Google and Oracle, doesn’t seem likely to improve much in the short term. How long did Google spend adding Java 7 functionalities little by little to Android? Years? Does anyone know if something is still missing? In the meantime, Java 8 arrived, lambdas and streams spread like wildfire, and in the mobile sector we continue to write lines and lines of paraphernalia to create a sad anonymous class. Has anyone tried writing RxJava without lambdas? Believe me, it's not nice the first time, and let's not talk about maintaining it afterward. Google was very conscious of this and, seeing that people were resorting to messy workarounds like using Retrolambda (which has always worked great for me, but let’s recognize that it’s a freak huge hack), ended up creating Jack, a new compiler that allowed some functionalities of Java 8. But, well, the problems of compatibility and performance have been so big that they recently threw it in the trash and incorporated these new features into the regular compiler. It was too little, too late, and we'll see if any judge does not pull it back. At Google they knew that it was not enough...
(My partner Miguel Sesma tells me that we don’t have reason to assume that support for Java 8 will not be complete. I say that since they are still announcing new features that are planned to be ported, I will believe it when I see it ... in 2021, at this rate.) Let’s recap. On Android Java is outdated, Groovy doesn’t run, Scala works as usual, and Ceylon (the work of another crack, Gavin King, creator of Hibernate and with Red Hat supporting it) is not there nor is expected to be.But Kotlin is made to work perfectly on mobiles from day one. Also, a gentleman named Jake Wharton (practically the official demigod for those of us who are dedicated to Android), notices it and recommends it in an internal report for his company. The rest is history...
[caption id="" align="aligncenter" width="460"]
Jake Wharton seducing the camera[/caption]
Furthermore, as a culmination of history, in the keynote of Google I/O 2017 they dropped a bombshell: Google gives official support to Kotlin as a first-level language for development in Android:
https://youtu.be/d8ALcQiuPWs
The cheers and applause from those of us who were watching it via streaming from the Campus Madrid must have been heard from the street, and Twitter and Slack turned into a party. People applauding a programming language, just look at that. Almost nothing.
Later they also announced that, besides being open source, Kotlin is donated to a non-profit foundation and so on. But that's something for the lawyers in the audience...
But what is Kotlin like?!
Okay, you still haven’t seen a line of code yet. But I think it was important to know the why of things and, if I have done my job well, you will now be eager to see some meat instead of just skimming over the examples.
What is Kotlin like? Kotlin is like picking up the book Effective Java and implementing almost all of its recommendations in a language. Think about everything that gave you headaches in your last project and how many laps you had to circle to make it good. Now see how to make it flawless from the first minute.
Disclaimer: The following code will not always be as idiomatic as it could be. The idea is to understand the difference, but do yourself a favour and do not use it as an example to put into production.
Nullable types
Does it happen to anyone else that more than half of the errors that you end up resolving are NullPointerException at some point? How do you know whether a method can return null or not? And can I pass it to a parameter? Hoare says unreservedly that the was his billion-dollar, and fifty years later it continues to be gospel truth.
In Java there are many ways to test it, including two or three libraries that compete among themselves with annotations of @Null, @Nullable, @Notnull, etc. which, if you remember to use them and the IDE that you use parses them, can save you from some blunders. Do you use them in your projects? Are they not… um… a pain?
It could be worse, it could be the official option of Java 8, the Optional type. (Option… Optional… ok, I’ll leave it.) Or, as I call it, the great missed opportunity of Java. Gentlemen of Oracle, was it so difficult to give a little syntactic sugar to this to make it a bit simple to use? Or, I don’t know, make it Serializable. Call me crazy, but when flamewars have been started about when and how Optional should be used and, more than anything, when it shouldn't, it means that it is not as polished as it could be.
Finally, in the end what we all do is look at Javadoc, pray that it is updated and, lastly, plant the if code.
Not with Kotlin. Kotlin skips all this.
Java
/**
* The typical example.
*
* @param name name to greet, can't be null
*/
public void helloWorld(String name) {
// We use trim to make it exciting
System.out.println("Hi, " + name.trim()); // NPE if null is entered
}
public void defensiveHelloWorld(String name) {
if (name != null) {
System.out.println("Hi, " + name.trim());
}
}
public void annotatedHelloWorld(@NotNull String name) {
System.out.println("Hola, " + name.trim());
}
public void optionalHelloWorld(Optional<String> name) {
// Hey, if you use Idea even the IDE complains about using Optional in a parameter!
System.out.println("Hi, " + name.map(String::trim).orElse("world"));
}
Kotlin
[code light="true"]
// A String can’t ever be null
fun helloWorld(name: String) {
println("Hi, " + name.trim())
}
// String? can, but the compiler forces you to check before using it
fun helloWorldWithIf(name: String?) {
if (name != null) {
println("Hi, " + name.trim())
}
}
// There are safe calls, that return null if the var is null
// And an Elvis operator, that returns a default value if the left argument is null
fun callSafeHelloWorld(name: String?) {
println("Hi, " + (name?.trim() ?: "world"))
}
// For interoperability, if a value comes from Java is supposed to be nullable, but the
// developer can swear that he’s sure it’s not… and if he’s wrong, a NPE will be thrown
// Up to you, if you think it’s a good idea
fun dangerousHelloWorld(name: String?) {
println("Hi, " + name!!.trim())
}
[/code]
Immutability
Another classic story of days spent debugging code. Is a variable supposed to be overwritten or not? We have the option of adding final to everything, but it is adding an extra word to, effectively, everything and most of the time it is not done. Bad developers!
With Kotlin you have to decide from the beginning. And perhaps you realize that mutability is the devil, as a rule. Good Kotlin!
Java
final String immutable = "This can’t be changed";
String mutable = "This can, but didn’t we think that it rather not? I can’t remember...";
Kotlin
[code light="true"]
val immutable = "This can’t be changed"
var mutable = "This can, and I’m saying explicitely that I’m counting on that"
[/code]
Properties
I hate JavaBeans. I hate them. You have to write the variables, the getters and the setters and everything in Javadoc, copying and pasting the same thing four times. But it’s done in case someone wants some logic, you’ll tell me. Okay, but could there not be a bit more concise notation?
Java
public class JavaBean {
// The name
// Don’t worry if you missed it, because I’m going to repeat it quite a bunch of times
private String name;
/**
* Constructor.
*
* @param name name
*/
public JavaBean(String name) {
this.name = name;
}
/**
* Returns the name.
*
* @return name
*/
public String getName() {
return name;
}
/**
* Sets the name.
*
* @param name name
*/
public void setName(String name) {
this.name = name;
}
}
Kotlin
[code light="true"]
/**
* Bean with a [name].
*/
class KotlinBean {
var name: String? = null
}
/**
* Bean with a [name] whose first letter is returned in uppercase.
*/
class KotlinBeanWithLogic {
var name: String? = null
// Do we want some logic? No probs
get() = name?.capitalize()
}
/**
* Bean with a [name] that it’s initialized with a parameter from the constructor.
*/
class KotlinBeanConstructor(var name: String)
fun usageExamples() {
// It’s used as easy as this
val bean: KotlinBean = KotlinBean()
bean.name = "sergio"
println(bean.name) // sergio
// Does the getter have some logic? So what!
// You realize you can introduce it afterwards without touching even a comma from
// the rest of the code, don’t you?
val bean2: KotlinBeanWithLogic = KotlinBeanWithLogic()
bean2.name = "sergio"
println(bean2.name) // Sergio
val bean3: KotlinBeanConstructor = KotlinBeanConstructor("Sergio")
println(bean3.name)
}
[/code]
No primitive types
In Java there are primitive types and objects. Sun did this for optimization. Do you know who are better than people when it comes to analysing code and optimizing it? Compilers, that's who. In Kotlin all data is an object, and derives from Any?. Arrays also. There are no special cases.
Type inference
Is it obvious which type is a variable or a function belongs to? Well, don’t put it on if you don’t want to.
[code light="true"]
var aString = "I’m a String"
[/code]
Equals
You've been programming for years, and you still sometimes make the mistake of putting == instead of equals, or vice versa. In Kotlin both things are the same.
Java
AbstractMap.SimpleEntry<Integer, String> apples =
new AbstractMap.SimpleEntry<>(2, "pieces of fruit");
AbstractMap.SimpleEntry<Integer, String> oranges =
new AbstractMap.SimpleEntry<>(2, "pieces of fruit");
System.out.println(apples.equals(oranges)); // True
System.out.println(apples == oranges); // False
Kotlin
[code light="true"]
val apples = Pair(2, “pieces of fruit”)
val oranges = Pair(2, “pieces of fruit”)
println(apples.equals(oranges)) // True
println(apples == oranges) // True
println(apples === oranges) // == from Java, False in this case
[/code]
Default values
Do we want a default value for a parameter in Java? Welcome to the party of polymorphism!
Java
public void helloWorld(String adjective, String name) {
// We all know that concatenation is not very efficient
System.out.println(String.format("Hi, %s %s", adjective, name));
}
public void helloWorld() {
helloWorld("cruel", "world");
}
public void helloWorld(String name) {
helloWorld(“bland”, name);
}
// Do you want a version with only the adjective? That would be another method with
// a String parameter.
// It’s impossible.
// You have to change the name of the method.
public void uglyHelloWorld(String adjective) {
helloWorld(adjective, "thing");
}
Kotlin
[code light="true"]
fun helloWorld(adjective: String=”cruel”, name: String ="world") {
println("Hi, " + adjective + " " + name)
}
fun usageExample() {
helloWorld("exciting") // Hi, exciting world
}
[/code]
Parameters by name
Wait, the last example was missing something. Can you call the method to leave the adjective as is and only change the name? Yes, we can!
[code light="true"]
fun usageExample() {
helloWorld(name = "day") // Hi, cruel day
}
[/code]
Data classes
Ok, We’ve seen properties, type inference, default values and parameters by name. It was all part of a plan to teach you the data classes because, if I ordered these examples by importance, for me it would clearly be in the Top 3 of Kotlin. Pay attention.
If defining properties in a class already saves you lines and lines of bureaucracy with getters, setters and documentation, let’s think about the fact that for the typical JavaBean, which we want to be identified by the data that it carries inside and is already there, you have to write its equals, its hashCode and its toString.
Because we never fail to verify that the contract between equals and hashCode is respected, obviously. After all, there are libraries that help you, and virtually all IDEs bring functions to generate them.
Of course, then we add a new field, we forget to maintain these two methods, we place an instance in a collection and then the party starts...
Let’s not stop here. Do the fields have to be mutable or immutable? Do we create a constructor or a Builder? And a method for making copies? We could say that we don’t need it and that we’ll see later on, but that’s a sure recipe to end up making spaghetti code, and you know it. It’s better to establish a practice from the beginning, isn’t it? Or could Kotlin save us the trouble?
Look at this comparison, which is one that hurts. To be honest, I could use Guava or Apache Commons, but it would barely get rid of two or three lines...
Java
public class JavaBean {
private String name;
private String surname;
public JavaBean(String name, String surname) {
this.name = name;
this.surname = surname;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getSurname() {
return surname;
}
public void setSurname(String surname) {
this.surname = surname;
}
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (!(o instanceof JavaBean)) return false;
JavaBean javaBean = (JavaBean) o;
if (name != null ? !name.equals(javaBean.name) : javaBean.name != null) return false;
return surname != null ? surname.equals(javaBean.surname) : javaBean.surname == null;
}
@Override
public int hashCode() {
int result = name != null ? name.hashCode() : 0;
result = 31 * result + (surname != null ? surname.hashCode() : 0);
return result;
}
@Override
public String toString() {
return "JavaBean{" +
"name='" + name + '\'' +
", surname='" + surname + '\'' +
'}';
}
public JavaBeanBuilder copy() {
return new JavaBeanBuilder(name, surname);
}
public static final class JavaBeanBuilder {
private String name = "José";
private String surname = "García";
public JavaBeanBuilder() {
}
private JavaBeanBuilder(String name, String surname) {
this.name = name;
this.surname = surname;
}
public static JavaBeanBuilder toJavaBean() {
return new JavaBeanBuilder();
}
public JavaBeanBuilder withName(String name) {
this.name = name;
return this;
}
public JavaBeanBuilder withSurname(String surname) {
this.surname = surname;
return this;
}
public JavaBean build() {
return new JavaBean(name, surname);
}
}
}
Kotlin
[code light="true"]
data class KotlinBean(var name = "José", var surname = "García")
[/code]
I repeat, the word data has added the following to the class:
- equals,
- hashCode,
- toString,
- copy, to create a new instance from it, changing some parameter if necessary.
To which we add, by pure Kotlin syntax, the default values in the parameters and the ease of calling the parameters by name.
[code light="true"]
fun iWantABabyBrother() {
var sergio = KotlinBean(“Sergio”, “Delgado”)
var baby = sergio.copy(name=”Raúl”)
}
[/code]
All this in one line! With no possibility of bugs! And it will always be up-to-date!
String Interpolation
By the way, we said that concatenation isn’t cool. But so far, I’ve used it in all the examples of Kotlin. This was just to go little by little. In reality what I would write is this:
Java
System.out.println(String.format("Hi, %s %s", adjective, name));
Kotlin
[code light="true"]
println("Hola, $adjective $name")
[/code]
Remember, make what’s right simple. How many times do we not get past the String.format because it’s not understandable or simply from laziness?
String literals
It’s not just interpolation, defining strings in Java also becomes very cumbersome. Between these two options, which version seems more readable?
Java
String aLongString = "One line\nTwo lines";
String anotherLongString = "For the lines to be noticed\n" +
"We split it in several literals\n" +
"And join them";
String json = “{\”name\”:\”Sergio\”,\n\”surname\”:\”Delgado\”}”;
Kotlin
[code light="true"]
var aKotlinLongString = """
No need to escape here
Line breaks are allowed
But the spaces on the left
Slip into the value
Comments are moderated and will only be visible if they add to the discussion in a constructive way. If you disagree with a point, please, be polite.
Tell us what you think.