From Java to Kotlin in 20 minutes ⚡️
Tags: java, kotlin, android, beginners
Published: 2021-05-14 16:55:07

What is the experience like as a Java developer to start programming in Kotlin?

I didn't remember, it was years ago for me!

Fortunately a mob-programming session with my colleagues gave me the chance to see again things with a beginner's mind.

Story time!

<!-- TOC -->
- [Show me the code!](#show-me-the-code)
- [Mob-programming](#mob-programming)
- [`Tools > Kotlin > Configure Kotlin in project`](#tools--kotlin--configure-kotlin-in-project)
- [Tell Java that `@ParametersAreNonnullByDefault`](#tell-java-that-parametersarenonnullbydefault)
- [PinGuesser: `Convert Java File to Kotlin File`](#pinguesser-convert-java-file-to-kotlin-file)
- [PinGuesserTest: `Convert Java File to Kotlin File`  and manual fixes](#pinguessertest-convert-java-file-to-kotlin-file--and-manual-fixes)
- [Use the Kotlin standard library](#use-the-kotlin-standard-library)
- [Replace stream() API with Kotlin stdlib](#replace-stream-api-with-kotlin-stdlib)
- [Make val, not var](#make-val-not-var)
- [Fail fast](#fail-fast)
- [Functional style](#functional-style)
- [List.fold()](#listfold)
- [Where do We Go From Here?](#where-do-we-go-from-here)

<!-- /TOC -->


## Show me the code!

To follow along, checkout the code.

You need to have [IntelliJ Community Edition](https://www.jetbrains.com/idea) installed. It's free!

On MacOS for example, that's `$ brew install intellij-idea-ce`

The code is here, and you can see all the changes described below [**in this pull-request**](https://github.com/jmfayard/from-java-to-kotlin/pull/1)

{% github https://github.com/jmfayard/from-java-to-kotlin %}

But first some context

## Mob-programming

My colleagues Sarah and Peter and I were doing in a session of [Mob programming](https://en.wikipedia.org/wiki/Mob_programming) 

The goal was to solve the [kata of The observed PIN](https://www.codewars.com/kata/5263c6999e0f40dee200059d/train/java), where an unreliable spy tells that he saw the PIN **1357** being used, but actually, he's not quite sure, each digit could be instead one of its neighbor on the keyboard layout. It could be **1357** but also for example **2357** or **1368.**

The project was a Java project built with Maven. It contains two files: `PinGuesser.java` and `PinGuesserTest.java`. It compiles and run the unit tests in a matter of seconds, not minutes like in many Android apps. That makes for a better developer experience IMHO.

We were using [IntelliJ's Code With Me](https://www.jetbrains.com/help/idea/code-with-me.html) to share the code.

We were doing well and had solved the Kata in Java, then had refactored it to a satisfactory state.

- _Sarah_ : Is there anything else we could improve?
- _Peter_ : I don't know, looks good to me.
- _Me_ : Well, we have 20 minutes left, why not rewriting the whole thing in Kotlin?
- _Sarah_ : Oh, I've heard about Kotlin but haven't had the chance to use it yet. 20 minutes though, do you think we can do it?
- _Me_ : Let's get started and see where it leads us!

## `Tools > Kotlin > Configure Kotlin in project`

- _Peter_ : Ok, so I have never done any Kotlin in my life, tell me what to do.
- _Me_ : There is a command IntelliJ called `Convert Java File to Kotlin File`. It's a great starting point!
- _Peter_ : Let's give it a try.

![https://user-images.githubusercontent.com/459464/118158571-42ef6000-b41c-11eb-89df-c32de3ffe8f0.png](https://user-images.githubusercontent.com/459464/118158571-42ef6000-b41c-11eb-89df-c32de3ffe8f0.png)


![https://user-images.githubusercontent.com/459464/118158602-4d115e80-b41c-11eb-8cb6-ee85143251ae.png](https://user-images.githubusercontent.com/459464/118158602-4d115e80-b41c-11eb-8cb6-ee85143251ae.png)

- _Peter_ : IntelliJ tells me that Kotlin is not configured, that makes sense.
- _Peter_ : How do I configure Kotlin in Maven?
- _Me_ : I don't know, I always used Gradle.
- _Me_ : Just let IntelliJ do it!
- _Me_ : By the way, what it will do is the same thing as `Tools > Kotlin > Configure Kotlin in project`
- _Peter_ : Let's do it
- _Peter_ : It seems to have worked. There are updates to the file `pom.xml`
- _Peter_ : [first commit](https://github.com/jmfayard/from-java-to-kotlin/pull/1/commits/caaab696fa50e57e5bcb7c0a23f70de29b43b480)

## Tell Java that `@ParametersAreNonnullByDefault`

- _Me_ : Before we try the Java to Kotlin converter, there is something we want to take are of.
- _Me_ : As you know, Kotlin has integrated nullability in the type system while Java by default has not.
- _Me_ : Therefore the converter is going to allow nulls everywhere, which is technically correct but not what you want.
- _Sarah_ : But there are annotations in Java to say if something is nullable or not, right?
- _Me_ : Exactly! And the one we want is to tell by default everything is non-null. Conveniently, it's exactly how it works in Kotlin too.

```diff
diff --git a/pom.xml b/pom.xml
     <dependencies>
+        <dependency>
+            <groupId>com.google.code.findbugs</groupId>
+            <artifactId>jsr305</artifactId>
+            <version>3.0.2</version>
+        </dependency>

+++ b/src/main/java/pin/package-info.java
@@ -0,0 +1,4 @@
+@ParametersAreNonnullByDefault
+package pin;
+
+import javax.annotation.ParametersAreNonnullByDefault;
```

## PinGuesser: `Convert Java File to Kotlin File`

- _Peter_ : I guess I now open `PinGuesser.java` and just relaunch the converter  `Convert Java File to Kotlin File`
- _Me_ : Correct
- _Peter_ : It seems that... it worked? There is a file `PinGuesser.kt`
- _Me_ : How do you know it worked, though?
- _Sarah_ : You should run the unit tests
- _Peter_ : Right

![https://user-images.githubusercontent.com/459464/117936889-aaff5280-b305-11eb-9c84-be7205e9673c.png](https://user-images.githubusercontent.com/459464/117936889-aaff5280-b305-11eb-9c84-be7205e9673c.png)

- _Peter_ : It's still all green. Amazing, I have written my first Kotlin code ever, and it is bug-free!
- _Sarah_ : Good job!
- _Peter_ : What about the tests? Shouldn't we convert those too?
- _Me_ : You don't need to. Java and Kotlin can co-exist peacefully in the same codebase.
- _Sarah_ : Ok, but it looks fun, I want to try it out too!
- _Peter_ : First let me [commit](https://github.com/jmfayard/from-java-to-kotlin/pull/1/commits/08f5f3d1d5f84a8746a671e3828c18d3d703ae06)

## PinGuesserTest: `Convert Java File to Kotlin File`  and manual fixes

- _Sarah_ : So I open `PinGuesserTest.java` and run the command. How is it called?
- _Peter_ : `Convert Java File to Kotlin File`
- _Sarah_ : Let's go!
- _Sarah_ : I now have a `PinGuesserTest.kt` . It has some errors though

![https://user-images.githubusercontent.com/459464/117937632-80fa6000-b306-11eb-931b-7642e4aac07a.png](https://user-images.githubusercontent.com/459464/117937632-80fa6000-b306-11eb-931b-7642e4aac07a.png)

- _Peter_ : Maybe apply the suggestion to optimize imports?
- _Sarah_ : Ok.
- _Sarah_ : It worked.
- Me : as you see it's not perfect, but it's an awesome learning tool: you start with what you already know (in Java) and see it converted in what you want to learn (in Kotlin)
- _Sarah_ : Let me run the unit tests
- _Sarah_ : I have some weird JUnit errors

![https://user-images.githubusercontent.com/459464/118160523-b3977c00-b41e-11eb-8151-a4dea10aa9e2.png](https://user-images.githubusercontent.com/459464/118160523-b3977c00-b41e-11eb-8151-a4dea10aa9e2.png)

- _Me_ : Ok, so I understand that. Java has **static** methods while Kotlin has the concept of a [companion object { ... }](https://kotlinlang.org/docs/object-declarations.html#companion-objects) 
- _Me_ : Its methods *look like* static methods but are a bit different. Here JUnit really wants static methods, and we need an annotation to make it happy

```diff
-        fun testSingleDigitParameters(): Stream<Arguments> {
+        @JvmStatic fun testSingleDigitParameters(): Stream<Arguments> {
             return Stream.of(
                 Arguments.of("1", java.util.Set.of("1", "2", "4")),
                 Arguments.of("2", java.util.Set.of("1", "2", "3", "5")),
@@ -61,7 +58,7 @@ internal class PinGuesserTest {
             )
         }
 
-        fun invalidParams(): Stream<Arguments> {
+        @JvmStatic  fun invalidParams(): Stream<Arguments> {
             return Stream.of(
                 Arguments.of("   "),
                 Arguments.of("A"),
```

- _Sarah_ : Unit tests now work!
- _Sarah_ : The project is now 100% in Kotlin
- _Sarah_ : [commit](https://github.com/jmfayard/from-java-to-kotlin/pull/1/commits/f4d60faa3949f21064edda02f69503d57e37941b)

## Use the Kotlin standard library

- _Peter_ : What comes next?
- _Me_ : It's possible to create `List`, `Set` and `Map` the traditional Java way, but the Kotlin standard library contains plenty of small utilities to streamline that, that would be my first change. Let me do it:

![https://user-images.githubusercontent.com/459464/118299183-b6f33c00-b4e0-11eb-9458-c8322d65cae9.png](https://user-images.githubusercontent.com/459464/118299183-b6f33c00-b4e0-11eb-9458-c8322d65cae9.png)

- _Me_ : that looks better. Are the unit tests still green?
- _Me_ : They are, let's [commit](https://github.com/jmfayard/from-java-to-kotlin/pull/1/commits/1a5c8dbe3415be22713b3f765f4050515bb91b72)

## Replace stream() API with Kotlin stdlib

- _Me_ : Something else contained in the Kotlin Standard Library are functions found in the functional programming languages like `.map()`, `.filter()`, `.flatmap()` and much more.
- _Sarah_ : A bit like the Java Stream API that we are using?
- _Me_ : Yes, like this but less verbose and more performant under the hood!

```diff
-    fun combineSolutions(pins1: Set<String>, pins2: Set<String>): Set<String> {
-        return pins1.stream()
-            .flatMap { pin1: String ->
-                pins2
-                    .stream()
-                    .map { pin2: String -> pin1 + pin2 }
-            .collect(Collectors.toSet())
-    }

+    fun combineSolutions(pins1: Set<String>, pins2: Set<String>): Set<String> =
+        pins1.flatMap { pin1 ->
+            pins2.map { pin2 ->
+                "$pin1$pin2"
+             }
+        }.toSet()
```

- _Sarah_ : Unit tests are still green.
- _Sarah_ : [commit](https://github.com/jmfayard/from-java-to-kotlin/pull/1/commits/82070f0b8e9a39c030da723ddb00f1510efb810c)

## Make val, not var

- _Me_ : Next, in idiomatic Kotlin style, we tend to use `val property` instead of `var property` most of the time.
- _Peter_ : What's the difference?
- _Me_ : `val property` is read-only, it has no setter, it's like a `final field` in Java
- _Peter_ : I see. So, I just change the var property with a val?
- _Me_ : Pretty much so.
- _Peter_ : Easy enough
- _Peter_ : [commit](https://github.com/jmfayard/from-java-to-kotlin/pull/1/commits/33b089cad9c90996f2800a0657c0f38d57ba992b)

## Fail fast

- _Sarah_ : Is there an idiomatic way to validate the parameters of a function?
- _Sarah_ : The PIN should be something like `7294` with all characters being digits
- _Me_ : Yes, you use `require(condition) { "error message" }`  for that
- _Sarah_ : How would that look here?

```kotlin
fun getPINs(observedPin: String): Set<String> {
    require(observedPin.all { it in '0'..'9' }) { "PIN $observedPin is invalid" }
    // rest goes here
}
```

- _Sarah_ : Thanks!
- _Sarah_ : [commit](https://github.com/jmfayard/from-java-to-kotlin/pull/1/commits/2424c592495db6f444e30708b88d9b8e2d26cd5e)

## Functional style

- _Sarah_ : What comes next?
- _Me_ : I would like to liberate the functions
- _Peter_ : What do you mean?
- _Me_ : Look, we have this `PinGuesser` class, but what it is doing exactly?
- _Me_ : It's doing nothing, it's a dumb namespace.
- _Me_ : It's a **noun** that prevents us for accessing directly the **verbs** who are doing the real work.
- _Me_ : One of my favorite programming language of all time is [Execution in the kingdom of nouns by Steve Yegge.](http://steve-yegge.blogspot.com/2006/03/execution-in-kingdom-of-nouns.html)
- _Sarah_ : I know that rant, pure genius!
- _Sarah_ : How do we free up the verbs/functions?
- _Me_ : We remove the class and use top-level functions

```kotlin
diff --git a/src/main/java/pin/PinGuesser.kt b/src/main/java/pin/PinGuesser.kt
index 17a20b3..38e457c 100644
--- a/src/main/java/pin/PinGuesser.kt
+++ b/src/main/java/pin/PinGuesser.kt
@@ -1,9 +1,5 @@
 package pin
 
-import java.util.stream.Collectors
-
-class PinGuesser {
-    companion object {
         val mapPins = mapOf(
             "1" to setOf("1", "2", "4"),
             "2" to setOf("1", "2", "3", "5"),
@@ -16,7 +12,6 @@ class PinGuesser {
             "9" to setOf("6", "8", "9"),
             "0" to setOf("0", "8"),
         )
-    }
 
     fun getPINs(observedPin: String): Set<String> {
         for (c in observedPin.toCharArray()) {
@@ -38,5 +33,4 @@ class PinGuesser {
             pins2.map { pin2 ->
                 "$pin1$pin2"
             }
-        }.toSet()
-}


--- a/src/test/java/PinGuesserTest.kt
+++ b/src/test/java/PinGuesserTest.kt
class PinGuesserTest {
-    val pinGuesser = PinGuesser()
 
     @ParameterizedTest
     @MethodSource("testSingleDigitParameters")
     fun testSingleDigit(observedPin: String?, expected: Set<String?>?) {
-        val actual = pinGuesser.getPINs(observedPin!!)
+        val actual = getPINs(observedPin!!)
         Assertions.assertEquals(expected, actual)
     }
```

- _Me_ : [commit](https://github.com/jmfayard/from-java-to-kotlin/commit/76273f16bd754477c5a558c003d8434b2cb28c98)

## List.fold()

- _Peter_ : Can we go a step back? What does it bring us to make the code nicer like this? At the end of the day, the customer doesn't care.
- _Me_ : Well, I don't know you, but often I don't really understand the code I'm supposed to work on. I tend to work hard to simplify it and at some point it fits in my head and the solution becomes obvious.
- _Peter_ : What would it looks like here?
- _Me_ : Now that the code is in a nice functional idiomatic Kotlin, I realize that the program can be solved using a single functional construct: **List.fold()**
- _Sarah_ : Show me the code
- _Me_ : [commit](https://github.com/jmfayard/from-java-to-kotlin/pull/1/commits/025fab52f042a3b077204594f8dd2237b8659304)

```kotlin
fun getPINs(observedPin: String): Set<String> {
    require(observedPin.all { it in mapPins }) { "PIN $observedPin is invalid" }

    return  observedPin.fold(initial = setOf("")) { acc: Set<String>, c: Char ->
        val pinsForChar: Set<String> = mapPins[c]!!
        combineSolutions(acc, pinsForChar)
    }
}

fun combineSolutions(pins1: Set<String>, pins2: Set<String>): Set<String> =
    pins1.flatMap { pin1 ->
        pins2.map { pin2 ->
            "$pin1$pin2"
        }
    }.toSet()
```

## Where do We Go From Here?

I hope that you liked this article.

If you want to get in touch, you are welcome to do so via https://jmfayard.dev/

The code is available at https://github.com/jmfayard/from-java-to-kotlin 

Start in the `java` branch and compare with what is the `kotlin` branch. [**See this pull-request**](https://github.com/jmfayard/from-java-to-kotlin/pull/1)

If you are interested to learn more about Kotlin, I've written about it here

{% link [https://dev.to/jmfayard/best-ways-to-learn-kotlin-from-scratch-or-from-java-with-books-or-tutorials-online-or-in-the-ide-52cm](https://dev.to/jmfayard/best-ways-to-learn-kotlin-from-scratch-or-from-java-with-books-or-tutorials-online-or-in-the-ide-52cm) %}