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 functionsCreating 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 castInstance 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 + listTwoJoin 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 GuavaInit 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 protectionInit 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 documentationDeprecation 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 PageDelegation 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