Creative Commons Licence This work is licensed under a Creative Commons
Attribution-ShareAlike 4.0 International License

Examples of Kotlin making your Java code better

Andy Balaam
artificialworlds.net/blog

Contents

Kotlin

Kotlin

Things that are good

Gentle nudges

Good patterns

Examples

Elliot John Gleave during radio interview (cropped)
Image by MRCZMT [CC BY-SA 3.0 (https://creativecommons.org/licenses/by-sa/3.0)], from Wikimedia Commons

Value objects

public class Thing { private final String name; private final String desc; public Thing(String name, String desc) { this.name = name; this.desc = desc; } public String getName() { return name; } // ...

Value objects

public class Thing { // ... @Override public int hashCode() { final int prime = 31; int result = 1; result = prime * result + ((desc == null) ? 0 : desc.hashCode()); result = prime * result + ((name == null) ? 0 : name.hashCode()); return result; } // ...

Value objects

public class Thing { // ... @Override public boolean equals(Object obj) { if (this == obj) { return true; } if (obj == null) { return false; } if (getClass() != obj.getClass()) { return false; } Thing other = (Thing) obj; if (desc == null) { if (other.desc != null) { return false; } } else if (!desc.equals(other.desc)) { return false; } if (name == null) { if (other.name != null) { return false; } } else if (!name.equals(other.name)) { return false; } return true; } // ...

Value objects

public class Thing { // ... @Override public String toString() { return "Thing(name=" + name + ", desc=" + desc + ")"; } // ...

Value objects

Value objects

data class ThingK(val name: String, val desc: String)

Parameters

public class Message { final String id; final String customer; final String to; final String from; final String body; public Message(String id) { this(id, null, null, null, null); } public Message forCustomer(String customer) { return new Message(... } public Message withTo(String to) { // ...

Parameters

Message m = new Message("234") .withTo("012") .forCustomer("Acme");

Parameters

data class MessageK( val id: String, val customer: String? = null, val to: String? = null, val from: String? = null, val body: String? = null )

Parameters

val m = MessageK("234", to="012", customer="Acme")

Null safety

public class Scorer { static final Map<String, Integer> wordValues = new HashMap<>(); static { wordValues.put("purple", 3); wordValues.put("lavender", 5); wordValues.put("amethyst", 50); } // ...

Null safety

public class Scorer { // ... final Map<String, Integer> scores = new HashMap<>(); void scoreRequest(String user, String path) { scores.merge( user, wordValues.get(path), Integer::sum ); } int total(String user) { return scores.get(user); }

Null safety

@Test public void Different_users_are_independent() { Scorer s = new Scorer(); s.scoreRequest("user1", "purple"); s.scoreRequest("user2", "purple"); s.scoreRequest("user1", "lavendar"); // CRASH assertThat(s.total("user1"), equalTo(8)); assertThat(s.total("user2"), equalTo(3)); }

Null safety

private val wordValues = mapOf( "purple" to 3, "lavender" to 5, "amethyst" to 50 )

Null safety

class ScorerK { val scores = mutableMapOf<String, Int>() fun scoreRequest(user: String, path: String) { scores.merge( user, wordValues[path] ?: 0, Integer::sum ) } fun total(user: String): Int { return scores[user] ?: 0 } }

Null safety

class ScorerK { val scores = mutableMapOf<String, Int>() fun scoreRequest(user: String, path: String) { scores.merge( user, wordValues[path] ?: 0, Integer::sum ) } fun total(user: String): Int { return scores[user] ?: 0 } }

Null safety

Immutability

public class Emitter { public void handleMessage(// ... long start, end; // ... start = System.currentTimeMillis(); String url = urls.get(m.to); if (url == null) { // ... } for(start = 0; start < 3; start++) { // ... } end = System.currentTimeMillis(); System.out.println(m.id + ": " + (end-start));

Immutability

public class Emitter { public void handleMessage(// ... final long start, end; // ... start = System.currentTimeMillis(); String url = urls.get(m.to); if (url == null) { // ... } for(start = 0; start < 3; start++) { ERROR! // ... } end = System.currentTimeMillis(); System.out.println(m.id + ": " + (end-start));

Immutability

class EmitterK { fun handleMessage(// ... val start: Long val end: Long // ... start = System.currentTimeMillis() val url = urls[m.to] if (url == null) { // ... } for(start in 0..3) { // ... } end = System.currentTimeMillis() println(m.id + ": " + (end - start))

Immutability

class EmitterK { fun handleMessage(// ... val start: Long val end: Long // ... start = System.currentTimeMillis() val url = urls[m.to] if (url == null) { // ... } for(start in 0..3) { // Warning: name shadowed // ... } end = System.currentTimeMillis() println(m.id + ": " + (end - start))

Immutability

Handling cases

long reattemptTs = NEVER; switch (code) { case 451: case 2020: reattemptTs = now + (ONE_HOUR * tries); break; case 711: case 1522: reattemptTs = now + ONE_MINUTE; break; case 2: default: reattemptTs = NEVER; break; } return reattemptTs;

Handling cases

return when (code) { 451, 2020 -> now + (ONE_HOUR * tries) 711, 1522 -> now + ONE_MINUTE 2 -> NEVER else -> NEVER }

Handling cases

sealed class Response(val code: Long); class Ok : Response(0) class FatalError(code: Long) : Response(code) class FastError(code: Long) : Response(code) class SlowError(code: Long) : Response(code) // ... return when (response) { is SlowError -> now + (ONE_HOUR * tries) is FastError -> now + ONE_MINUTE is FatalError -> null is Ok -> null }

Handling cases

return when (code) { 451, 2020 -> now + (ONE_HOUR * tries) 711, 1522 -> now + ONE_MINUTE 2 -> NEVER else -> NEVER }

Constructors

class Requester { Requester(String scheme, String host, HttpClient client) { this.scheme = scheme; this.host = host; this.client = client; } Requester( String user, String pass, String host, boolean useSsl) { this(useSsl, host, new SimpleClient(user, pass)); } Requester(boolean useSsl, String host, HttpClient client) { this(useSsl ? "https" : "http", host, client); }

Constructors

class RequesterK( val scheme: String, val host: String, val client: HttpClient ) { constructor( user: String, pass: String, host: String, useSsl: Boolean) : this(useSsl, host, SimpleClient(user, pass)) constructor( useSsl: Boolean, host: String, client: HttpClient) : this(if (useSsl) "https" else "http", host, client)

Constructors

Streams

static final List<String> hotDrinks = Arrays.asList("tea", "coffee"); public List<String> hotCustomers() { return customers.stream() .filter( cust -> hotDrinks.stream().anyMatch( drink -> cust.drink.contains(drink) ) ) .map(Customer::getName) .collect(Collectors.toList()); }

Streams

val hotDrinks = listOf("tea", "coffee") fun hotCustomers(): List<String> { return customers .filter { cust -> hotDrinks.any { cust.drink.contains(it) } } .map {it.name} }

Summary

Wish list

More info

Videos youtube.com/user/ajbalaam
peertube.mastodon.host/accounts/andybalaam
Social @andybalaam on Twitter
@andybalaam@mastodon.social
Web artificialworlds.net/blog
artificialworlds.net
Code github.com/andybalaam
gitlab.com/andybalaam