Kotlin, just another trend? (Spoiler: no)

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.

The first great debate on languages of History

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…

Jake Wharton seducing the camera

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:

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

// 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())
}

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

val immutable = "This can’t be changed"
var mutable = "This can, and I’m saying explicitely that I’m counting on that"

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

/**
* 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)
}

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.

var aString = "I’m a String"

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

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

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

fun helloWorld(adjective: String=”cruel”, name: String ="world") {
   println("Hi, " + adjective + " " + name)
}
 
fun usageExample() {
   helloWorld("exciting") // Hi, exciting world
}

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!

fun usageExample() {
   helloWorld(name = "day")  // Hi, cruel day
}

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

data class KotlinBean(var name = "José", var surname = "García")

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.

fun iWantABabyBrother() {
   var sergio = KotlinBean(“Sergio”, “Delgado”)
   var baby = sergio.copy(name=”Raúl”)
}

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

println("Hola, $adjective $name")

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

var aKotlinLongString = """
   No need to escape here
   Line breaks are allowed
   But the spaces on the left
   Slip into the value
   <--- These spaces
"""
 
var aKotlinLongStringWithTrim = """
   |Don’t you worry
   |Kotlin has an answer for everything
   |A pipe is the default prefix
   |But we could use another chat
""".trimMargin()
 
var json = “””
{ 
	// What a change, isn’t it?
“name”: “Sergio”,
	“surname”: “Delgado”
}
“””

Extension Methods

We all have a StringUtils class or two. Too bad that this is not object orientation or anything that resembles it. An extension method allows you to add code to a class that you can’t touch, so that it is read as God intended.

fun String.sayHello() {
   println("Hi, $this")
}
 
fun usageExample() {
   "world".sayHello()
}

Don’t use it for such shabby examples. But do add it to your Android Contexts and Activities and marvel at the difference.

Functions and lambdas

You’ve seen that the methods all have the reserved word fun in front. Effectively, it means function. Why is it added? Because Kotlin allows you to have functions outside of objects. Also to define a function with a lambda, or a reference to another function. And to pass functions as parameters of other higher-order functions, without the need to invent interfaces. Functions are fun, fun, fun!

fun higherOrderFunction(aString: String, function: (String) -> String) {
   println(function(aString))
}
 
fun firstLetterToUppercase(aString: String): String {
   return aString.capitalize()
}
 
// An easier way to say the same
fun firstLetterToUppercaseAsAExpression(aString: String) = aString.capitalize();
 
fun usageExamples() {
    // Without an instance, because the function is defined in the package
   higherOrderFunction("sergio", ::firstLetterToUppercase)  
 
   // A function from a class
   higherOrderFunction("sergio", String::capitalize)    
  
   // A lambda, in the most awkward way possible
   higherOrderFunction("sergio", { aString: String -> aString.capitalize()})
  
   // If the last parameter is a lambda, you can take it out of the parameter list
   higherOrderFunction("sergio") { aString: String -> aString.capitalize() }
 
   // And if there is only one parameter, it can be named “it” by default
  // Can you imagine your Android listeners like this?
   higherOrderFunction("sergio") {
       it.capitalize()
   }
 
}

Collections and ranges

We have higher-order functions. We have lambdas. We have classes with immutable data. Does this not sound like functional programming? No one forces us, but if we want to use it, we can. And for that, a good covering of the library collections is nothing short of essential.

val numbers = listOf(1, 2, 3, 4)
numbers.filter {
   // Even numbers are out
   it % 2 == 0                  
}.map {
   // Multiply by 2
   it * 2                       
}.forEach {
   // And print them to the console
   println("Result: $it")    
}
 
val aMap = hashMapOf("one" to 1, "two" to 2, "three" to 3)
val aString = map.filter {
   // Every pair is destructured into two variables, and we just ignore the key
   (_, num) -> num % 2 != 0      
}.map {
   // We now throw the value in the bin
   (nombre, _) -> nombre         
}.joinToString(", ")
// And we get "one, three"

There are also ranges, which are not only used in fors.

for (i in 1..10) {
   println(i)
}
 
val j = 3
if (j in 1..10)
   println("$j is between 1 and 10")
}

There are even sequences, which are like collections but with lazy evaluation and are potentially infinite.

// It will generate every power of 2, beginning with a seed value and a function to calculate
// every subsequent value from the previous one
generateSequence(1) { it * 2 }
       // We take the first 5
       .take(5)
       // And collect them into a list
       .toList()

Semicolons

You will have noticed that semicolons are optional in Kotlin. Apparently, many people hate them. First world problems.

And there are no bad things?

Well, to be honest, there are some:

  • Not very widespread. Up until this week, Kotlin was known by a minority, albeit an enthusiastic one. But you’ll already see the number of views on YouTube of the two conferences they’ve dedicated to it on the Google I/O…
  • It is not mathematically perfect. For the Scala people, who don’t miss an opportunity to criticise the rest of us.
  • Tools are lacking. For example, you will have noticed that we can’t format Kotlin source code in the blog to make it look as nice as Java. Also, even though I’m a big fan of the code analysis of IntelliJ Idea, we don’t have an analyser like Sonarqube to place it in our continuous integration process. For now.

There is much left to say

Indeed, we have barely scratched the surface. I hope, however, to have left you wanting more. See you, if you fancy, in future blog articles where we’ll be able to talk about:

  • More features of the language. Even more? Yes, even more, but in exchange I won’t bore you with my stories. Sealed classes! inline functions! Delegation! Singletons! Generics! Co-routines! DSLs!
  • Enough with bits of code, here we will make a complete tutorial to create an app from scratch and without using any Java]. You’ll like it, I assure you.
  • Did you not remember that Kotlin also works for the browser? Will it succeed where Google failed with Dart? It will be difficult, but perhaps an example with React Native will make you take their side.
  • Spring Boot. A micro-service with Spring Boot 1.5 works perfectly, and I will show it to you, but did you know that there is official support of the language in Spring 5 and Spring Boot 2.0? The future of Kotlin is bright in the backend.
  • Gradle this substitute for Maven produces a love-hate response in me. On the one hand, well, it isn’t Maven, but I would be lying if I said that I haven’t spent hours reviewing Groovy scripts to find some option that wasn’t defined exactly where it should be and that derailed my entire compilation. Damn dynamic languages! I can’t be the only one frustrated by this, because the people at Gradle are working hard to allow defining scripts completely in Kotlin. Hallelujah.
  • Testing if your boss is a coward and doesn’t let you put Kotlin into production, consider that he probably doesn’t care how the hell you do the tests. JUnit 4, JUnit 5, Mockito, Spek, Kluent… We can handle it all.
  • Kotlin Native. They’re ambitious at Jetbrains. Not only do they want us to share code between JVM and JavaScript, but now they encourage platforms with native compilation and interoperability with C. With Kotlin Native they point to IoT devices or to the iPhone, although for now we have a functional preview for Raspberry Pi. They take away the garbage collector (which they promise to replace) and the standard library of Java, but in return they give us … the world. One language to rule them all!
  • More frameworks, if you haven’t already had enough. ¿VertX? ¿Kara? ¿ktor? Of course, I can’t promise that any of these will still be alive next year. Except Vert.X, I’m afraid.
  • And many more surprises… I hope.

And if I can’t wait?

Well, in the meantime, here are some resources of all kinds and colours:

But the most important thing if you want to learn is… roll up your sleeves and get to it. Come on. It isn’t so difficult.

2 comentarios

  1. Jon Latane says:

    You’re actually mistaken in your equality example because you used Kotlins Int type which compiles to the JVM int type (Int? maps to Integer). I tested it in the Kotlinc REPL just to be sure. It should read:

    println(apples === oranges) // == from Java, TRUE in this case

    • Sergio Delgado says:

      Good catch, it’s because the JVM caches these simple values and assigns the same instance to both variables. The same can happen with String literals, i.e.

      I’ve changed the example to use a regular class. Thanks for the shout out!

Escribe un comentario