Java for the New Millenium

Brewing Since 1995

  • Over 9 million developers
  • #1 most demanded language for jobs
  • Easy to find talent
  • Reliable enough

Why Change?

Conclusion:

Languages Affect Quality!

What's wrong with Java quality?

Issues

  • Null is a problem everywhere
  • Classes default as non-final
  • Libraries cannot be extended
  • Code is mostly boilerplate
  • Checked exceptions were a mistake

Null:

"The Billion-Dollar Mistake"

- Sir Tony Hoare

In Mother Kotlin,

Types don't default nullable.

If you allow null,

unsafe code is caught at compilation.

Solutions

  • Safe call operator - ?.
  • Elvis operator - ?:
  • Danger, Will Robinson - !!
  • Safe cast operator - as?
  • FilterNotNull for Collections

Classes Default Final

Why Does This Matter?

Once open, a class can never be closed.

Other code becomes dependent.

Design and document for inheritance or else prohibit it. (#14)

But Java Libraries ARE Final?!

I can't add my utility methods.

Welcome to the contradiction of Java!

BUT

I can depend on your class signature so it breaks my code.

Kotlin Has Extension Methods!

myDate.isItAfterTheDeadline()

myActivity.showSnackBarMessage()

myString.pigLatinize()

It's so useful!

  • Getters
  • Setters
  • HashCode
  • Equals
  • ToString
  • Copy

Or?

Immutable Data Classes

No boilerplate. Your compiler handles it.

Design patterns are a symptom that your language is broken.

-- Jeff Atwood

Singletons?

BUILT IN!

Delegation?

BUILT IN!

Type-Safe Builders?

HOT DAMN!

Async/Await? Coroutines?

NOW WE'RE COOKING WITH GAS!

Checked Exceptions:

A Failed Psychology Experiment

try { doSomething(); }

catch (HorrificException e) {

  // TODO write an error handler

  // maybe after writing my novel,

  // winning a Guinness record, &

  // learning the digits of pi

}

Other Awesomeness!

  • Best in Class IDE Support
  • 100% Compatible with Java
  • Can compile to JVM, Android, or JS
  • Fast compile times like Java
  • Lambdas and Functions are First Class

Lombok vs. Kotlin

Everything Lombok Can Do,

Kotlin Can Do Better

https://goo.gl/qKYLhf

Let's See Code!

// Just import and call as static methods!

// Kotlin:
package demo
class Foo
fun bar() {}

// Java:
new demo.Foo();
demo.Foo.bar();

// use @JvmField annotation to expose fields

// boxed Java types map to Nullable types i.e. String?

Kotlin Interop (Java)

// Just import and call it!

import java.util.Calendar

fun calendarExample() {
  val calendar = Calendar.getInstance()
}

// Compile-time null checks are reduced for Java code
// without nullability annotations. Duh!

Java Interop (Kotlin)

public class Customer {
    private String name;
    private String email;

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public String getEmail() {
        return email;
    }

    public void setEmail(String email) {
        this.email = email;
    }

    @Override
    public boolean equals(Object o) {
        if (this == o) return true;
        if (o == null || getClass() != o.getClass()) return false;

        Customer customer = (Customer) o;

        if (name != null ? !name.equals(customer.name) : customer.name != null) return false;
        return email != null ? email.equals(customer.email) : customer.email == null;

    }

    @Override
    public int hashCode() {
        int result = name != null ? name.hashCode() : 0;
        result = 31 * result + (email != null ? email.hashCode() : 0);
        return result;
    }

    @Override
    public String toString() {
        return "Customer{" +
                "name='" + name + '\'' +
                ", email='" + email + '\'' +
                '}';
    }

    public Customer copy() {
        Customer copy = new Customer();
        copy.name = name;
        copy.email = email;
        return copy;
    }

}

Creating a Data Object (Java)

data class Customer(val name: String,
                    val email: String)

// DONE!
// 
// This created:
// getters and setters
// equals()
// hashcode()
// toString()
// copy()
// and copy() with changed fields
// among other functions

Creating a Data Object (Kotlin)

    public boolean isCustomerSaved(String name, String email) {
        // ...
    }
    
    public boolean isCustomerSaved(String email) {
        isCustomerSaved("Default", email);
    }
    
    public boolean isCustomerSaved() {
        isCustomerSaved("Default", "default@example.com");
    }

    public boolean isCustomerSaved(String name) {
        isCustomerSaved(name, "default@example.com);
    }

Default parameters (Java)

fun isCustomerSaved(name: String = "Default",
  email: String = "default@example.com") { 
      //...
  }

Default parameters (Kotlin)

// Java 7
Iterator<Foo> it = collection.iterator();
while( it.hasNext() ) {
  Foo foo = it.next();
  if( !condition(foo) ) it.remove();
}

// Java 8
collection.stream()
    .filter(p -> condition)
    .collect(Collectors.toList());

Filtering a List (Java)

// long form
list.filter { x -> x > 0 }

// short form
list.filter { it > 0 }

Filtering a List (Kotlin)

String string = String.format("Name %s", name);
System.out.println(string);

String Interpolation (Java)

println("Name $name")

String Interpolation (Kotlin)

// if then version
if (x instanceof Foo) {
    ((Foo)x).fooMethod();
} else  if (x instanceof Bar) {
    ((Bar)x).barMethod();
} else {}

// evil reflection, just don't
CLAZZ z = CLAZZ.valueOf(x.getClass().getSimpleName());
switch (z) {
  case "Foo":
    ((Foo)x).fooMethod();
     break;
  case "Bar":
    ((Bar)x).barMethod();
     break;
  default:
}

Instance Checks (Java)

when (x) {
    is Foo -> x.fooMethod()
    is Bar -> x.barMethod()
    else   -> castException()
}

// no need to cast 'x' to call methods
// that's called smart cast

Instance Checks (Kotlin)

Iterator it = mp.entrySet().iterator();
while (it.hasNext()) {
    Map.Entry pair = (Map.Entry)it.next();
    System.out.println(pair.getKey() + " = " + pair.getValue());
}

Traversing a Map (Java)

for ((k, v) in map) {
    println("$k -> $v")
}

Traversing a Map (Kotlin)

List<String> newList = new ArrayList<String>();
newList.addAll(listOne);
newList.addAll(listTwo);

Join Two Lists (Java)

val newList = listOne + listTwo

Join Two Lists (Kotlin)

myList.sort(new Comparator<MyObject>() {
    @Override
    public int compare(MyObject left, MyObject right) {
        return left.getDate().compareTo(right.getDate());
    }
});

Sort By Property (Java)

val sortedList = myList.sortedBy { it.date }

Sort By Property (Kotlin)

public class Pair<T, U> {         
    public final T t;
    public final U u;

    public Pair(T t, U u) {         
        this.t= t;
        this.u= u;
     }
 }

Pair<String, Int> getResult() {
    // ...
    return new Pair<String, Int>();
}

Pair<String, Int> myPair = getResult();

Return Multiple Values (Java)

data class Result(val result: Int,
                  val status: Status)

fun getResult(): Result {
    // ...    
    return Result(result, status)
}

val (result, status) = getResult()
// This is called a destructuring declaration.
// It accepts an arbitrary number of parameters.

Return Multiple Values (Kotlin)

// umm, import Guava with 15,000 Android-busting methods?

import com.google.common.collect.ImmutableList;

ImmutableList<String> myList = ImmutableList.of("a", "b", "c");

Init Immutable List (Java)

val list = listOf("a", "b", "c")

// this will disallow modification at compile time
// but Java code can still modify it at run time

// it's a read only view of a mutable collection
// with compile-time write protection

// for truly immutable lists, use Guava

Init Immutable List (Kotlin)

// umm, import Guava with 15,000 Android-busting methods?

import com.google.common.collect.ImmutableMap;

ImmutableMap<String,String> myMap = ImmutableMap.of(
        "key1", "value1", 
        "key2", "value2", 
        "key3", "value3");

Init Immutable Map (Java)

val map = mapOf("a" to 1, "b" to 2, "c" to 3)

// same exception applies
// this is a read only view of a mutable map
// with compile time write protection

Init Immutable Map (Kotlin)

private int p = null;

// later in the code...
alterP();

public void alterP() {
    if (p == null) {
        p = computeP();
    }
}

Lazy Property (Java)

val p: String by lazy {
    computeP()
}

Lazy Property (Kotlin)

public class StringWrapper extends String {
    // FATAL ERROR: Cannot inherit from final java.lang.String
}

// okay try again...

public class StringUtil {
    public static String spaceToCamelCase(String string) {
        // ... this sucks!
    }
}

StringUtil.spaceToCamelCase(string);
// How many util classes do I have to import?

Extension Functions (Java)

fun String.spaceToCamelCase() { ... }

"Convert this to camelcase".spaceToCamelCase()

Extension Functions (Kotlin)

public static ThreadSafeSingleton getInstanceUsingDoubleLocking(){
    if(instance == null){
        synchronized (ThreadSafeSingleton.class) {
            if(instance == null){
                instance = new ThreadSafeSingleton();
            }
        }
    }
    return instance;
}

// This breaks on reflection, serializable, and rare situations.

// There is no one canonical singleton.

Create Singleton (Java)

object Resource {
    val name = "Name"
}

Create Singleton (Kotlin)

if (data != null) {
    System.out.println(String.format("%s", data.get("email")));
} else {
    System.out.println("default");
}

// OR...

System.out.println(data != null ?
    String.format("%s", data.get("email"))
    : "default");

Use if non-null or else (Java)

println(data?.email ?: "default")

Use if non-null or else (Kotlin)

String call() {
    if (something()) {
        return doSomething();
    else {
        throw new UnsupportedOperationException("TODO: DoSomethingElse?");
}

// Thanks to Jake Wharton for the tips going forward!

Explosive Placeholders (Java)

fun call : String {
    if (something()) {
        return doSomething()
    else {
        TODO("DoSomethingElse?") // exception
}

Explosive Placeholders (Kotlin)

public static String join(String sep, List<String> strings) {
    if (sep == null) { 
        throw new NullPointerException("sep == null");
    }
    if (sep.length < 2) {
        throw new IllegalArgumentException("sep.length < 2:" + sep);
    }
    if (strings == null) {
        throw new NullPointerException("strings == null");
    }
    // ...
}

Input Validation (Java)

fun join(sep: String, strings: List<String>) {
    require(sep.length < 2) {"sep.length < 2: " + sep}
    // ...
}

// there are other built-in validation functions like:
// requireNotNull(),
// check(),
// checkNotNull(),
// and assert()

Input Validation (Kotlin)

long startTime = System.currentTimeMillis();
doSomething();
long endTime = System.currentTimeMillis();
System.out.println(
    String.format("took %sms", endTime - startTime)
);

Timing Code (Java)

val timeInMillis = measureTimeMillis{doSomething()}
println("took ${timeInMillis}ms")

// function is inlined and fast
// there's also measureTimeNanos()

Timing Code (Kotlin)

@Deprecated
public static String join(String sep, List<String> strings) {
    // ...
}

// The JavaDoc leaves you dying for documentation

Deprecation Levels (Java)

@Deprecated("Use strings.joinToString(sep).")
fun join(sep: String, strings: List<String>) {
    // ...
}

// Mandatory string directs user to better option.

// Can set DeprecationLevel = ERROR or
// DeprecationLevel = HIDDEN to keep
// existing code working, but stop new users.

Deprecation Levels (Kotlin)

// use automated IDE suggestion-based replacement
@Deprecated("Use strings.joinToString(sep).",
    replaceWith = 
        ReplaceWith("strings.joinToString(sep)"))
fun join(sep: String, strings: List<String>) {
    // ...
}

// even works with imports!
@Deprecated("Use Guava's joiner.",
    replaceWith =
        ReplaceWith("Joiner.on(sep).join(strings)",
    imports = "com.google.common.base.Joiner"))
// ...

Deprecation Auto (Kotlin)

class CopyPrinter implements Copy, Print {

    private Copy copier;
    private Print printer;

    public CopyPrinter(Copy copier, Print printer) {
        this.copier = copier;
        this.printer = printer;
    }

    @Override
    public Page copy(Page page) {
        return copier.copy(page);
    }

    @Override
    public void print(Page page) {
        printer.print(page);
    }
}

interface Copy {
    Page copy(Page page);
}

interface Print {
    void print(Page page);
}

class Page {}

Delegation Pattern (Java)

class CopyPrinter(copier: Copy, printer: Print) 
    : Copy by copier, Print by printer

interface Copy {
    fun copy(page: Page): Page
}

interface Print {
    fun print(page: Page)
}

class Page

Delegation Pattern (Kotlin)

Wrap Up,

Damn It!

Conclusion:

Languages Affect Quality!

Start a Kotlin Group?

Questions?

Kotlin: Java for the New Millenium

By Colin Lee

Kotlin: Java for the New Millenium

  • 1,191