Part 2 : Picking Kotlin for Android — Killing Features

Welcome to Part 2, this part includes lots of Java vs Kotlin sample code snippets as a show case. Many useful comments/arguments are written in the code block. In order to have better reading experience, I would recommend you to use a large screen to continue to read this article.

Compile-time Null Safe

“Compile-time Null-Safe” in Kotlin is a serious and fundamental improvement compared with Java. It solves the infamous null problem in Java and gives a very productive way to write high quality and NPE-less code.

In Java, every object you created will be assigned it to null by default. It is also known as “The Billon dollar mistake”. Numerous “workarounds” has been brought out in Java, third-parties library or even in IDE, for example:

  • @NotNull
  • Objects.requireNonNull(..)
  • Optional.ofNullable(..)

Closely look at the follow Java example, it shows four different ways to write the same functionality.

// Java null risk workaround example

import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;

import java.util.Optional;
import static java.lang.System.*;

class NullRisk {
  // Bad code in Java
  public String doRiskyThing(String str) {
    return str.toLowerCase();
  }
  
  // Human error by typing wrong annotation, should be @NotNull
  public String doMissLeadingThing(@Nullable String str) {
    return str.toLowerCase(); // IDE warning
  }
  
  // possible work around, IDE will show warning if the input is null
  // but it is safe to compile
  @NotNull
  public String doSafeThing(@NotNull String str){
    return str.toLowerCase();
  }

  // possible work around, but using Optional in java is pain and not handy as Kotlin/Scala
  public Optional<String> doOptionalThing(@Nullable String strOpt) {
    return Optional.ofNullable(strOpt).map(String::toLowerCase);
  }

  {
    // compile OK! throw NullPointerException in run-time
    out.println(doRiskyThing(null));
    
    // compile OK! IDE warning, throw NullPointerException in run-time
    out.println(doSafeThing(null)); 
    
    // compile OK! throw NullPointerException in run-time
    out.println(doMissLeadingThing(null));
    
    // compile OK!
    out.println(doOptionalThing(null));
  }
}

The first function doRiskyThing is written w/o @NotNull annotation. The function is not self-documented and you must do some workaround to improve it.

If we focus on the function declaration public String doRiskyThing(String riskyString)without looking at the implementation details, developers won’t get any information about the input parameter. Questions may come up for Java developers :

  • Can I pass a null?
  • if I pass a null , what will be the return? null? empty string? other default string?

Consider if developers are required use doRiskyThing to build something, they had to spend extra effort to run a test or look at the source code to build up their confidence.

The other two examples are the popular “Work Arounds”, ONLY doOptionalThing(String strOpt) is safe in run-time.

In Kotlin, nullable object must be represented by appending ? . For example,Int? String? Long? DateTime? BigDecimal? . This syntax will be checked in compile time and also in IDE inspection.

No workaround is needed, and the follow functions are by-design self-documented.

// Kotlin null safe example
// Java null risk example 

fun doSth(str: String): String {
    return str.toLowerCase()
}

fun doSthOptional(str: String?): String? {
    // Optional chainning, similiar to Swift
    return str?.toLowerCase()
}

doSth(null) // compile error
doSth("Abc") // return "abc"

doSthOptional(null) // return null
doSthOptional("Abc") // return "abc"

// More example on copmile-time checking
var a0 // fail, compile error  
var a1 = null // fail, compile error  
var a2: String = null // fail, compile error  
var a3: String? = null // OK

println(a3) // OK, print null  
println(a3.toLowerCase()) // fail, compile error  
println(a3?.toLowerCase()) // OK, print null

As you can see the fundamental difference in this tiny example, why don’t Java fix “the billion dollar mistake”?

Java has to maintain the Backward Compatibility

So, you may ask what is the point that related to Kotlin?

In 2016 Aug, we are going use existing Java libraries to write a NEW Android application or NEW backend application. Considering the fact that both of Java & Kotlin can 100% using the existing Java libraries. If we pick Java instead of Kotlin, we have to sacrifice the beauty of compile-time Null-safe and do a lot of extra “work-arounds” in order to cope with the trade off (Backward compatibility) that we don’t even need ?

Kotlin sounds interesting? Null-Safe is only one of the major killing features.
There are at least 5+ killing features I am going to introduce.

Cautions/Disclaimer: If you keep reading the following Kotlin’s killing features, you will never want to go back to Java. Trust me, think twice before you do!

Zero Overhead Nullable Type, Nullable Chaining and Default Value

Java 8 has introduced a new Optional<T> Class into java.util package. As Optional<T> is just a value type (container) to wrap a value.

However, the lack of pattern matching in Java (A very power features in Scala), unwrapping and comparing the wrapped value are damn pain in Java. Optional<T> is proposed to be used ONLY for return type to raise the awareness of “no result”, hence, avoid NPE. Check to see the following example

// Optional Type Overhead
Optional<String> strOpt = null; // Compile OK

Optional<String> strOpt = Optional.empty(); // Compile OK

strOpt = null; // Compile OK

strOpt == null; // Compile OK => true

strOpt = Optional.of("gary"); // Compile OK

strOpt = Optional.ofNullable("gary"); // Compile OK

// cannot direct assign a value
strOpt = "gary"; // Compile error: Type mismatch
  
// cannot direct compare with a value
strOpt.equal("gary"); // Compile error: Type mismatch
  
strOpt.equal(Optional.of("gary")); // Compile OK, true
  

// unwrap and compare the wrapped value
if(strOpt.isPresent()) {
  final String str = strOpt.get();
  return str.equal("gary");
} 
else {
  return false
}

// do something when the wrapped value is not null
strOpt.ifPresent(str -> {
  doSth(str)
});
  
// Easy to be mis-used
// in some case,
// null is a good representation of nothing instead of empty String
String str = strOpt.orElse("");

// throw NullPointerException if the wrapped value is null
String str = strOpt.get();
Java Optional Overheads example

Let’s see a simple example of Scala pattern matching (FYI):

// pattern matching in Option Type
val usernameOpt: Option[String] = Some("gary")

usernameOpt match {
    case Some("gary") => println("matched gary!")
    case Some("peter") => println("matched peter!")
    case Some(username) => println("found ${username}, but no username is matched")
    case None => println("No username is found")
}
Scala Option pattern matching

Let’s see how powerful is Kotlin’s Nullable Type:

// Kotlin NullableType is different from java.util.Optional or scala.Option
// it is NOT a Value Type

var str: String? = null

str == null // Compile OK! => true 

str = "gary"  // Compile OK! 

str == null // Compile OK! => false
str == "gary" // Copmile OK! => true

// Optional chaining
str?.toUpperCase() == "GARY" // Compile OK! => true
str?.toLowerCase()?.capitalize() == "Gary" // Compile OK! => true

// Default value `?:`
val username = str ?: "N/A" // username is String now!

// Kotlin Smart cast - if
if(str is String) {
   // the Type of str is smart casted to `String`!!!
   println(str.toUpperCase())
}

// Kotlin Smart cast - when
when(str) {
  is String -> println(str.toUpperCase()) 
  else -> println("str is null")
}
Kotlin Nullable Type

From my experience, refactoring in Java/Scala from a Type to java.util.Optional/scala.Option Type is a disaster and seems like rewriting a whole part of the affected codes. But refactoring Kotlin’s Nullable Type is nearly zero-effort.

Type Inference

Type inference polishes the beauty of static-typed language and can boost the coding productivity to reach as fast as dynamic-typed level.

Java and Kotlin are both static and strong typing language. This characteristic is very important to large scale application.

Good Things Come With a Price

In Java, compared with dynamic and weak typing language like PHP or Javascript, you have to spend extra effort to specify the type definition for variables.

Sometimes, the type definitions are meaningless and verbose because

  • It is too obvious to Human
  • The compiler should clever enough to know the type
  • Type definitions increase the cost of refactoring.

For example, when we create a map with seed values with type key: String value: Integer

// Java 9
final Map<String,Integer> abc = Map.of("a",1, "b", 2, "c", 3);

// Java >= 5
final Map<String,Integer> abc = new HashMap<String, Integer>() {{  
    put("a",1);
    put("b",2);
    put("c",3);
}};

final Integer c = abc.get("c");

// Question: 
// If you are asked to refactor from Map<String,Integer> to Map<String,String>,
// try to estimate the efforts!
Verbose Java

In Kotlin,

let the compiler do it for you

We can skip type definition and let the compiler make type inference for us.

// val abc: Map<String,Int> = mapOf("a" to 1, "b" to 2, "c" to 3)
// the actual type of the Map is obvious
// thus we can skip Map<String,Int> and let kotlin compiler to make type inference
val abc = mapOf("a" to 1, "b" to 2, "c" to 3)

val abcd = abc + ("d" to 4) // ok!
val abce = abc + ("e" to "5") // compile error: Type mismatch

// val c: Int? = abc["c"]
var c = abc["c"] 

c = "2" // compile error: Type mismatch

// Same Question: 
// If you are asked to refactor from Map<String,Integer> to Map<String,String>,
// try to estimate the efforts!

The syntax is very similar to dynamic typed language but any type-mismatched operations would be considered as compilation error.

Kotlin with type inference can help us to write less-verbose code without sacrifice anything.

Default Argument & Named Argument

In a real world, there exist some situations we damn need default value instead of null. Java developers can either do a very ugly work around if-null-then-default value or do a verbose method overloading. For example,

// Ugly work around to save time (compared to method overload)
void doSomething(
  @NotNull String fname,
  @NotNull String lname,
  @Nullable String addr,
  @Nullable Gender gender
) {  
  requireNonNull(fname);
  requireNonNull(lname);
  if(addr == null) addr = "N/A";
  if(gender == null) gender = Gender.Unknown;
  // do sth
}
String lastName = "lastName";
String firstName = "firstName";
String gender = Gender.Male;
String address = "address";

// As we know the implementation will provide default value for null
// so we type null in the parameter...
// code-smell? WTF by new comer?
// Question: What is the null suppose to be?
doSomething(firstName,lastName,null,null);  
doSomething(firstName,lastName,address,null);  
doSomething(firstName,lastName,null,gender);  
doSomething(firstName,lastName,address,gender);

// without named paramenter...
// Can you notify the bug when you type out this?
// it is safe to compile but logically error 
// bug: lastName and firstName are swapped
doSomething(lastName,firstName,address, gender); 

// To avoid null and expose a better API
// without default and namged argument, method overloading is the only way
void doSomething(String fname, String lname) {
  doSomething(fname,lname,"N/A", Gender.Unknown);
}
void doSomething(String fname, String lname, String addr) {
  doSomething(fname,lname,addr, Gender.Unknown);
}
void doSomething(String fname, String lname, Gender gender) {
  doSomething(fname,lname,"N/A", gender);
}
void doSomething(String fname, String lname, String addr, Gender gender) {
  // do sth
}

// Question: 
// if you asked to add one more arugment let say "Phone"
// how many overloading is needed?
  
Without Named & Default Arguments

Either the “ugly work around” and the “verbose method overloading” are also not self-documented, hard to refactor/change in Java.

Kotlin support Named and Default Argument, it helps developers to create a more intuitive, meaningful and maintainable API.

// Default argument
fun doSomething(
  fname: String,
  lname: String,
  addr: String = "N/A",
  gender: Gender = Gender.Unknown
) {
  // do something that really need fname,lname, addr, gender..
}

val firstName = "firstName"
val lastName = "lastName"
val address = "Address"
val gender = Gender.Male

// Normally you can do this
doSomething(firstName, lastName, address, gender) // OK!
doSomething(firstName, lastName, address)  // OK!
doSomething(firstName, lastName)  // OK!

// Fail, compile error! Type mismatch! Expected String type
doSomething(firstName, lastName, gender) 

// It can be solved by Named Argument
// with Named Argument
doSomething(
  fname = firstName,
  lname = lastName,
  addr = address,
  gender = gender
) // OK!

// skip the address (3rd argument)
doSomething(
  fname = firstName,
  lname = lastName,
  gender = gender
) // OK!

// Order is not important!!
doSomething(
  addr = address,
  lname = lastName,
  fname = firstName
) // OK!

// Fail! compile error: lname is required
// because lname has no default value
doSomething(
  addr = address,
  fname = firstName
)

// Ask yourself:
// Is it possible to achieve the same level of conciseness in Java?
Named & Default Arguments

As the gist example has clearly demonstrate the power of Named and Default Argument. This is a fundamental and serious improvement in Kotlin compared with Java.

Once you get used to the Kotlin’s powerful Named and Default Argument to design your interface, you won’t go back to Java.

Extension is Awesome

In Java, we can not directly add functionalities on a Class. If some logics applied on a specific Type and have to be used multiple time in a project, Java developers get used to create a XXXUtils for Type XXX in order to follow the D.R.Y. principle.

Programming has no magic. All about Abstractions

Kotlin provided a much better abstraction to model the similar problems that are solved by “Java Utils Pattern” — Extension

Traditional Java Utils usage

// please focus on the function name & declaration 

// As we all know, String is a final class and we can't extend
class StringUtils {
  public static TypeA toTypeA(String str)
}

// Assume TypeA is from third party library that you can't extend
class TypeAUtils {
  public static Double calculateResult(TypeA a) 
}

// Similar to String, you can't extend Double
class DoubleUtils {
  public static Double toTwoDecimalPlace(Double d) 
}

String someString = "someString";

// The execution order is not as same as to our reading direction(top-to-bottom)
// it is difficult to read
final Double typeAResultWithRounding = 
  DoubleUtils.toTwoDecimalPlace(
    TypeAUtils.calculateResult(
      StringUtils.toTypeA(someString)
    )
  );

// flatten it?  better to read but more unnessecary variables are created
final TypeA typeA = StringUtils.toTypeA(someString);
final Double typeAResult = TypeAUtils.calculateResult(typeA);
final Double typeAResultWithRounding = DoubleUtils.toTwoDecimalPlace(typeArResult);
  
// Question: 
// How do a new comer would know to use exactly these three Utils?
// Normally, they would ask someone or implement a duplicate one for their use case.
Utils Pattern

How is Kotlin provided a better abstraction for this problem? We can directly add an “Extension” for a specific Type with a very simple syntax! (Very similar to javascript)


// Kotlin

// extension, give toTypeA() functionality to String
fun String.toTypeA(): TypeA { 
  return TypeA(str = this)
}

// give calculateResult() functionality to TypeA 
fun TypeA.calculateResult(): Double {
  return this.str.length + 0.123456789
}

// give toTwoDecimalPlace() functionality to Double
fun Double.toTwoDecimalPlace(): Double {
  return BigDecimal(this).setScale(2, RoundingMode.HALF_UP).toDouble()
}
val someString = "someString"

// Better than Utils pattern
// 1. By-design chainning from top-to-bottom which is better for reading
// 2. Extension are statically import(Unlike Swift), extension usages are tracable.
// 3. IDE autocomplete works well to identify extensions on a Type,
val typeAResultwithRounding = someString
                                .toTypeA()
                                .calculateResult()
                                .toTwoDecimalPlace()

// Even new comer would see the extra functionality in the IDE auto-complete list!!
Kotlin Extensions

“Java Utils Pattern” is not IDE-friendly, new comer won’t notice some functionalities are already implemented in the XXXUtils while Kotlin’s Extension is completely solved this problem.

Here are some screenshots to demonstrate the IDE auto-complete with the Kotlin extension

With Kotlin extension, you would never have to ask

Is that XXXUtils.someFunc() is implemented already?

and never have to search in the .utils folder. 100% of time you will find it in the auto-completed list.

Handy Data Class

In a strongly-typed language, we will create numerous “Value Object” to hold data by a explicit type to classify their domain. For example, API Response, API Request, Data Model, Proxy Object, etc.

It is a very common practice and should be as simple as possible.

After I have met with Scala and recently with Kotlin. The only Java impression on creating a “Value Object”:

Java is too verbose and too costly to be used for Functional Programming (Immutable approach)

Start reading the following Kotlin data class to get some insight and don’t forget to read the comment :) .

data class User(
  // primary construtor
  val id: UUID,
  val username: String,
  val firstName: String? = null,
  val lastName: String? = null,
  val address: String? = null,
  val isEmailVerified: Boolean = false
) {
  // getter only
  var fullName: String get() {
    return "${firstName} ${lastName}"
  }

  // a function inside a User
  fun doSth() {
    println("do something")
  }
}

// By default, Kotlin has generated very useful constructor for a data class
// with the power of the Named and Default Argument
// We can use it in the following ways

// firstName, lastName, address, isEmailVerified is not required
User(
  id = UUID.randomUUID(),
  username = "garylo123"
)

// but you can still override the default value
val gary = User(
  id = UUID.randomUUID(),
  username = "garylo123",
  firstName = "Gary",
  lastName = "LO",
  address = "HK Somewhere"
)

println(gary.username) // print "garylo123"
println(gary.fullName) // print "Gary LO"

gary.doSth() // print "do something"

// developer-friendly for debuging
println(gary) // print "User(id=229c7b64-7dd3-4b5f-80bd-c88dc15ed13e, username=garylo123, firstName=Gary, lastName=LO, address=HK Somewhere, isEmailVerified=false)"



// Data class provide a handy function .copy for pure immutable object
// if we want to update the address of `gary`, we create a new copy
// original gary won't be mutated 
val updatedUser = gary.copy(address = "Hong Kong Somewhere")
Kotlin Data Class

In Java, developers have to use comparatively poor way (mutable or builder pattern) to create an complex Object. For example,

// Only used for holding the Data. 
class MutableJavaDataObject {
  @NotNull UUID id;
  @NotNull String username;
  @Nullable String firstName;
  @Nullable String lastName;
  @Nullable String address;
  bool isEmailVerified = false
}

// Problem?
// 1. All properties are mutable
// 2. No Constructor, how do others know `id` and `username` should NOT be null?
// 3. In Java, One file can only contain one Class.
//    Even a class is just a few line....
Mutable Java Objects

How we use it in Java ?

// MutableJavaDataObject, https://gist.github.com/gaplo917/deee7a16bf9f60c69cfedb47884820fa

// created by empty contructor
MutableJavaDataObject mjdo = new MutableJavaDataObject();

// assign the value directly
mjdo.id = UUID.randomUUID();
mjdo.address = "Hong Kong";

// printing the class reference is useless in 99% of time
System.out.print(mjdo) // print "MutableJavaDataObject@xxxxxxx"

// bugs? missed to assign the mjdo.username but it is compile time safe


// Question:
// A safer way? Builder pattern? Seriously!? for every data object? 

// Using Kotlin would cost you the same amount of time 
// that you write MutableJavaDataObject (work around),
// but gives much cleaner, robuster and safer `data class`
Mutable Java Objects usaged

Kotlin provides a very handy data class that

  • leverage the power of Name and Default Argument on constructor
  • provide handy .copy for writing pure immutable object
  • is developer-friendly for debugging

It helps to reduce the verbosity of getter, setter and multiple constructor. What’s more, data class is by-default ready to be used in some popular JSON Serialization library such as GSON, Jackson.

If you are familiar with Java, without Name and Default Argument, you know that it is not feasible to create a “Value Object” that can achieve the same level of convenience as Kotlin's data class does.

“Kotlinize” Java Library

In a large scale application, consistency is the most important role to keep source codes more predictable and maintainable.

By default, Kotlin won’t encourage to create getter/setter function for a variable. Each variable has a get()/set(value) to implement:

class User {
    var name: String? = null
      get() {
        println("getting a username = ${field}")
        return field
      }
      set(nName) {
        println("changing to a new username ${nName} from ${field}")
        field = nName 
      }
  
}

fun main() {
    val user = User()
    
    user.name = "Gary LO"
    
    println("We get the username = ${user.name}")
    
    // Expected console output: 
    // changing to a new username Gary LO from null
    // getting a username = Gary LO
    // We get the username = Gary LO
}
Kotlin Getter/Setters

Therefore, in pure Kotlin code we never writegetXXX() or setXXX(value). But, nearly 100% of Java library would use getter/setter.

How does Kotlin solve this discrepancy? Let see the “TextView” in Java Android library.

Standard Android TextView(Java) in Kotlin

Kotlin will Kotlinize the Java “default” getter/setter into Kotlin style, you would never feel uncomfortable to use Java libraries.

If you are experience in using Java libraries in Scala, you must damn want this features, at least I do lol.

Better Functional Programming Support

Writing functional style code in Java is comparatively much more expensive than in Kotlin. Because Kotlin provided the following features:

  • Type Inference
  • Inexpensive Data Class (Immutable .copy features)
  • Intuitive Type declaration for Functions/Lamdas, i.e.(Int)-> Int , (Int,Int) -> Bool instead of java.util.functions.BiFunction<T,U,R>...
  • Support Destructing
  • Every thing is an expression (easier to write declarative code)
// `to` is a infix function to create a Pair<L,R>
val pair = 1 to "one"

// decompose the pair
val (num,str) = pair
Easy to create Pair & destructing a pair
val list = listOf(1,2,3,4,5)

// destructing the list
val (a,b,c) = list

println("a = $a, b = $b, c = $c") 
// a = 1, b = 2, c = 3

// already include in kotlin std-lib
val head = list.head
val tail = list.tail

println(head) // 1
println(tail) // [2,3,4,5]
Destructing a List
data class User(val id: Int, val name: String, val address: String)

val user = User(id = 1, name = "GARY LO", address = "Hong Kong")

// Destructing order is import!!
val (userId, userName, userAddress) = user

println("id = $userId, name = $userName, address = $userAddress")
// id = 1, name = GARY LO, address = Hong Kong
Destructing a data class
val list = listOf(1,2,3,4,5)

// destructing the list
val (a,b,c) = list

println("a = $a, b = $b, c = $c") 
// a = 1, b = 2, c = 3

// already include in kotlin std-lib
val head = list.head
val tail = list.tail

println(head) // 1
println(tail) // [2,3,4,5]
Destructing a List
// Kotlin - currying function example

fun doCurrying(first: Int): (Int) -> ((Int, Int) -> Int) -> Int {
  return { second -> { f -> f(first,second) } }
}

val add = { a: Int, b: Int -> a + b }
val multiply = { a: Int, b: Int -> a * b }
val minus = { a: Int, b: Int -> a - b }

val curriedFour = doCurrying(4)
val curriedFourFive = curriedFour(5)

curriedFourFive(add) // 9
curriedFourFive(multiply) // 20
curriedFourFive(minus) // -1
Currying function example
// Every Thing is an expression
val input = 1

val result = if(input == 1) "Equal to one" else "Not Equal to One"

val result2 = when(input) {
   1 -> "Equal to one"
   else -> "Not equal to one"
}

val result3 = try {
    input / 0
    "Can be calculated"
  } catch(e: ArithmeticException) {
    "Can't be calculated"
  }

println(result)  // Equal to one
println(result2) // Equal to one
println(result3) // Can't be calculated
Every thing is an expression

Major goodies (aforementioned)

  • Compile-time Null Safe
  • Zero Overhead Nullable Type, Nullable Chaining and Default Value
  • Type Inference
  • Default Argument & Named Argument
  • Extension is Awesome
  • Handy Data Class
  • “Kotlinize” Java Library
  • Better Functional Programming Support

Minor goodies

  • Intuitive String Template
  • Lots of handy function are implemented in Kotlin std-lib i.e. array.find
  • Multiple class or object in single file
  • Support legacy JVM. Kotlin Runs on Java 6 and its interface can have default implementation just like in Java 8.
  • Best IDE Support

Next — Part 3 : Picking Kotlin for Android — Swift in Android?