2017: kotlin - now more than ever
TRANSCRIPT
2017: KOTLIN - NOW MORE THAN EVER! KAI KOENIG (@AGENTK)
AGENDA
AGENDA
▸ What is Kotlin?
▸ Idioms and language concepts
▸ The 1.1 release
▸ Where does that leave Android
▸ Outside the box
▸ Final thoughts
WHAT IS KOTLIN?
WHAT IS KOTLIN?
SOME KOTLIN FUNDAMENTALS
▸ Statically typed programming language for JVM and Android
▸ Started as internal language “Project Kotlin” at Jetbrains in July 2010
▸ Kotlin 1.0 since Feb 2016
▸ Now: Open-Source, Apache License
▸ Named after an island in the Gulf of Finland
HISTORY OF KOTLIN (I)
▸ Jetbrains wanted a more efficient JVM language when building products
▸ Looked at Scala, Groovy, etc, but came up with their own language spec
▸ Shown first at the JVM Language Summit in 2011
▸ Got some traction in Android-land in 2014
▸ modern language features (lambdas, HOF etc) but Java 6-bytecode
▸ Low methods count (~7000)
▸ Strong focus on concision and an efficient, bloat-free language
WHAT IS KOTLIN?
HISTORY OF KOTLIN (II)
▸ Since 1.0 release: multiple maintenance releases up to 1.0.7
▸ 1.1 was released in Feb 2017, now at 1.1.1
▸ Kotlin/JS is a first-class deployment/compilation target now
▸ Coroutines
▸ Some stdlib cleanup
▸ Strong community, lots of interesting frameworks, awesome support from Jetbrains
WHAT IS KOTLIN?
HOW DOES A SIMPLE CONVERSION LOOK LIKE?public String listConvert(Collection<Integer> collection) { StringBuilder sb = new StringBuilder(); sb.append("{"); Iterator<Integer> iterator = collection.iterator(); while (iterator.hasNext()) { Integer element = iterator.next(); sb.append(element); if (iterator.hasNext()) { sb.append(", "); } } sb.append("}"); return sb.toString(); }
fun listConvert(collection: Collection<Int>): String { val sb = StringBuilder() sb.append("{") val iterator = collection.iterator() while (iterator.hasNext()) { val element = iterator.next() sb.append(element) if (iterator.hasNext()) { sb.append(", ") } } sb.append("}") return sb.toString() }
fun listConvertKt(collection: Collection<Int>): String { return collection.joinToString(prefix = "{",postfix = "}") }
WHAT IS KOTLIN?
IDIOMS & LANGUAGE PATTERNS
https://www.flickr.com/photos/geraldford/6976818221/
IDIOMS & LANGUAGE PATTERNS
IDIOM AND LANGUAGE OVERVIEW
▸ Immutability
▸ String templates
▸ Null safety
▸ Properties and Fields
▸ Data classes
▸ Extension functions
▸ Syntactic sugar
▸ Type inference
▸ Lambdas
▸ Collection API
▸ Type-safe builders
▸ Java-Kotlin-Interop
IDIOMS & LANGUAGE PATTERNS
IMMUTABILITY
▸ Support for mutable and immutable variables, properties and fields
▸ Keywords var and val
▸ val - immutable (recommended)
▸ var - mutable
▸ Similar concept applies for class properties
val a: Int = 1 val b = 1
val c: Int c = 1
val x = 23
// fails x += 1
STRING TEMPLATES
▸ Kotlin Strings can contain template expressions
▸ String templates start with a $ character and
▸ can contain simple references such as $s, as well as
▸ complex expressions in curly braces: ${s.length}
val s = "abc" val str = "$s.length is ${s.length}"
Output:
abc.length is 3
IDIOMS & LANGUAGE PATTERNS
NULL SAFETY
▸ An explicit way to deal with NPEs
▸ Nullable types vs non-nullable types:
▸ String: no nullable
▸ String?: nullable
▸ Handle manually
▸ Use Safe Call operator ?.
▸ !! operator to allow/trigger a NPE
// Won't compile var lastName: String = null
// Will compile var lastNameNullable: String? = null
// Will also not compile println(lastNameNullable.length)
// Option 1 (-1) println(if (lastNameNullable != null) lastNameNullable.length else -1)
// Option 2 (null) println(lastNameNullable?.length)
// Option 3 (NPE) println(lastNameNullable!!.length)
IDIOMS & LANGUAGE PATTERNS
PROPERTIES AND FIELDS
▸ Kotlin classes have mutable or immutable properties
▸ Default getter/setters for properties, can be customised
▸ An automated backing field can be provided by the compiler (if required)
▸ Alternative: use an explicit backing property
var counter = 0 set(value) { if (value >= 0) field = value }
public class Address { public var name: String = ... public var street: String = ... public var city: String = ... public var state: String? = ... public var zip: String = ... }
IDIOMS & LANGUAGE PATTERNS
DATA CLASSES
▸ The POJOs/Beans of other languages
▸ Data classes implicitly create:
▸ getters/setters - recommend to use val as often as possible.
▸ useful implementations for equals(), hashCode(), toString(), copy()
▸ copy() has default parameters and can be used to alter a copy
data class ChromeEncryptedPayload( val encryptedPayload: String, val encryptionHeader: String, val cryptoKeyHeader: String)
val pl = ChromeEncryptedPayload( encryptedPayload = "...", encryptionHeader = "...", cryptoKeyHeader = "...")
val anotherPl = pl.copy( encryptedPayload = "...")
IDIOMS & LANGUAGE PATTERNS
SYNTACTIC SUGAR
▸ When-statement
▸ Ranges / Loops
▸ Destructuring
▸ Default and named arguments
IDIOMS & LANGUAGE PATTERNS
fun whatIs(x: Any) { when (x) { is Int -> println(x + 42) is String -> println(x.length) is IntArray -> println(x.sum()) } }
whatIs(4) // 46 whatIs("4") // 1 whatIs(intArrayOf(1,2,3,4,5)) // 15
if (i in 1..10) { println(i) }
val (name, age) = person fun read(b: Array<Byte>, off: Int = 0, len: Int = b.size()) { … }
EXTENSION FUNCTIONS
▸ Allow adding new functionality to a class without inheritance or decorators
▸ Kotlin has extension functions as well as extension properties
▸ Resolved statically, don’t actually modify the class
fun Int.sum(otherInt: Int): Int = this + otherInt
3.sum(7)
fun Activity.toast(message: CharSequence, duration: Int = TOAST.LENGTH_SHORT) { Toast.makeText(this, message, duration).show() }
// In onCreate of an Activity override fun onCreate(...) { ... toast("Hi there") ... }
IDIOMS & LANGUAGE PATTERNS
LAMBDAS
▸ Anonymous function or “function literal”
▸ Closely related to the ideas of Higher-Order-Functions
val sum = { x: Int, y: Int -> x + y }
val sum: (Int, Int) -> Int = { x, y -> x + y }
private fun convertPetListToDomain(list: List<Pet>): List<PetDomain> { return list.map { convertPetItemToDomain(it) } }
private fun convertPetItemToDomain(pet: Pet): PetDomain { return PetDomain(pet.age,pet.size,pet.id,pet.name,pet.sex,pet.breed) }
IDIOMS & LANGUAGE PATTERNS
TYPE-SAFE BUILDERS (I)
▸ Builders are a very popular approach in Groovy to define data in a declarative way
▸ Often used for:
▸ generating XML or JSON
▸ UI layout (Swing components) etc
▸ In Kotlin, builders even can be type-checked
JsonBuilder builder = new JsonBuilder()
builder.records { car { name 'HSV Maloo' make 'Holden' year 2006 country 'Australia' } }
String json = JsonOutput.prettyPrint (builder.toString())
IDIOMS & LANGUAGE PATTERNS
TYPE-SAFE BUILDERS (II)
▸ html() is a function with a lambda as an argument (init)
▸ init’s type is a function type with receiver, this allows you to:
▸ pass receiver (of type HTML) to function
▸ call members of instance inside the function
fun result(args: Array<String>) = html { head { title {”HTML in Kotlin"} } body { ... } }
fun html(init: HTML.() -> Unit): HTML { val html = HTML() html.init() return html }
IDIOMS & LANGUAGE PATTERNS
TYPE-SAFE BUILDERS (III)
▸ Html class has functions to build the head and the body elements of the DSL.
▸ Not shown: classes further down in the hierachy:
▸ Head, Body etc.
▸ Complete HTML builder example at: http://goo.gl/TndcC9
class Html { ...
fun head(headBuilder: Head.() -> Unit) { head = Head() head?.headBuilder() }
fun body(bodyBuilder: Body.() -> Unit) { body = Body() body?.bodyBuilder() } }
IDIOMS & LANGUAGE PATTERNS
THE 1.1 RELEASE
THE 1.1 RELEASE
OVERVIEW (I)
▸ JavaScript target is not experimental anymore (browser & NodeJS)
▸ Full Kotlin language support
▸ Large part of the stdlib is supported
▸ Coroutines (JVM, experimental)
▸ Lightweight concurrency mechanism
▸ Alternative to threads
▸ Tooling improvements
THE 1.1 RELEASE
OVERVIEW (II)
▸ Language features
▸ Type aliases
▸ Bound callable references
▸ More powerful data classes
▸ Destructuring in lambdas
▸ Property syntax
▸ Underscore-parameters for unused variables in lambdas
THE 1.1 RELEASE
COROUTINES (I)
▸ Asynchronous programming is becoming increasingly important
▸ Problem: need to avoid blocking introduces a lot of complexity
▸ Kotlin’s approach: Suspending functions
▸ Function/lambda that can be suspended and resumed
▸ Ideas similar to cooperative multi-tasking
▸ Minimal integration into the core language and stdlib, most of functionality provided by libraries
THE 1.1 RELEASE
COROUTINES (II)
▸ Using coroutines requires explicit import of the Kotlin coroutines libraries
▸ Suspending function can use other suspending functions like delay()
▸ join() waits for the background job on doWorld() to complete.
▸ Light-weight, can act like daemon threads
fun main(args: Array<String>) = runBlocking<Unit> { val job = launch(CommonPool) { doWorld() } println("Hello,") job.join() }
// this is a suspending function suspend fun doWorld() { delay(1000L) println("World!") }
THE 1.1 RELEASE
TOOLING
▸ General approach: avoid tooling to be bound to language releases
▸ But: there’s some new stuff in 1.1:
▸ Updates for all major IDE plugins
▸ Incremental compilation is now a first-class citizen
▸ Linting for Kotlin in Android Studio projects was improved
TYPE ALIASES
▸ Allow to define alternative names for types
▸ Often used with generic types
▸ Different from import aliases
typealias OscarWinners = Map<String, String>
fun countLaLaLand(oscarWinners: OscarWinners) = oscarWinners.count { it.value.contains("La La Land") }
fun checkLaLaLandIsTheBestMovie (oscarWinners: Map<String, String>) = oscarWinners["Best picture"] == "La La Land"
import domain.PetModel as domainPets
THE 1.1 RELEASE
CLASSES
▸ Kotlin 1.1 removes restrictions around data classes and sealed classes
▸ Sealed: don’t need subclassed nested into them anymore
▸ Data: can now extend other classes
THE 1.1 RELEASE
sealed class Expr { class Const(val number: Double) : Expr() class Sum(val e1: Expr, val e2: Expr) : Expr() object NotANumber : Expr() }
fun eval(expr: Expr): Double = when(expr) { is Expr.Const -> expr.number is Expr.Sum -> eval(expr.e1) + eval(expr.e2) Expr.NotANumber -> Double.NaN }
sealed class Expr data class Const(val number: Double) : Expr() data class Sum(val e1: Expr, val e2: Expr) : Expr() object NotANumber : Expr()
fun eval(expr: Expr): Double = when (expr) { is Const -> expr.number is Sum -> eval(expr.e1) + eval(expr.e2) NotANumber -> Double.NaN }
DESTRUCTURING LAMBDAS
▸ Easier unpacking arguments passed to a lambda
THE 1.1 RELEASE
val map = mapOf(1 to "one", 2 to "two")
// before println(map.mapValues { entry -> val (key, value) = entry "$key -> $value!" })
// now println(map.mapValues { (key, value) -> "$key -> $value!" })
COMPATIBILITY
▸ Kotlin 1.1 is backwards compatible to 1.0 on the language and stdlib level
▸ Code that compiled and ran in 1.0 will compile and run in 1.1
▸ More on compat: http://kotlinlang.org/docs/reference/compatibility.html
THE 1.1 RELEASE
WHERE DOES THAT LEAVE ANDROID?
WHERE DOES THAT LEAVE ANDROID?
IT’S STILL JAVA 6
▸ All the new features still work on Java 6
▸ Can be used and deployed to pretty much any current Android device/version
WHERE DOES THAT LEAVE ANDROID?
COROUTINES
▸ At first glance they look like a JVM- or server-side-only feature
▸ However, there are at least 3 libraries around to make coroutines useful in Android:
▸ Anko (support for coroutines since 0.10 beta)
▸ kotlinx-coroutines-android (support for UI operations)
▸ AsyncAwait
WHERE DOES THAT LEAVE ANDROID?
COROUTINES IN ANKO
▸ Add anko-coroutines dependency
▸ Earlier versions of Anko already had support for async handling
▸ New:
▸ Coroutines in listeners
▸ asReference()
▸ bg()
fun getData(): Data { ... } fun showData(data: Data) { ... }
async(UI) { val data: Deferred<Data> = bg { // Runs on the background getData() }
// This code is executed on the UI thread showData(data.await()) }
WHERE DOES THAT LEAVE ANDROID?
COROUTINES WITH KOTLINX-COROUTINES-ANDROID
▸ Centred around launch(UI) metaphor
▸ Launching coroutines in UI context:
▸ allows updating the UI from inside the coroutine and
▸ allows invoking suspending functions at the same time
▸ no frozen UI during delay()
▸ cancel() on coroutine in UI context allows stopping it
▸ Similar libraries exist for JavaFX and Swing
WHERE DOES THAT LEAVE ANDROID?
COROUTINES WITH KOTLINX-COROUTINES-ANDROID
fun setup(hello: TextView, fab: FloatingActionButton) { val job = launch(UI) { for (i in 10 downTo 1) { hello.text = "Countdown $i ..." delay(500) } hello.text = "Done!" } fab.setOnClickListener = { job.cancel() } }
WHERE DOES THAT LEAVE ANDROID?
COROUTINES WITH ASYNCAWAIT
▸ Async/await approach
▸ Very rich library on top of Kotlin’s coroutine core:
▸ awaitWithProgress
▸ try/catch/onError/finally
▸ Plugins for Retrofit and rxJava
async { val result = await { //Long running code } // Use result }
async { val repos = await { github.getRepos() } showList(repos) repos.forEach { repo -> val stats = await { github.getStats (repo.name) } showStats(repo, stats) } }
class AnimalListAdapter constructor (val items: PetList, val listener: ItemClickListener : RecyclerView.Adapter<AnimalListAdapter.ViewHolder>() {
... class ViewHolder(itemView: View) : RecyclerView.ViewHolder(itemView) { fun bind(item: Item, listener: ItemClickListener { itemView.setOnClickListener(listener) } }
}
typealias ItemClickListener = (View) -> (Unit)
WHERE DOES THAT LEAVE ANDROID?
TYPE ALIASES
▸ RecyclerView and adapter classes: Replace repeating types with a “click listener” aliasclass AnimalListAdapter constructor (val items: PetList, val listener: (View) -> Unit) : RecyclerView.Adapter<AnimalListAdapter.ViewHolder>() {
... class ViewHolder(itemView: View) : RecyclerView.ViewHolder(itemView) { fun bind(item: Item, listener: (View) -> Unit) { itemView.setOnClickListener(listener) } }
}
WHERE DOES THAT LEAVE ANDROID?
TOOLING
▸ Since 1.1beta2: Support for the Jack toolchain (ironic, given that that’s all gonna change again soon)
▸ Some new AS/IntelliJ intentions:
▸ Generating View constructors
▸ Merging if-statements and more
OUTSIDE THE BOX
https://www.flickr.com/photos/sillygwailo/5990089210/
OUTSIDE THE BOX
WHAT ELSE CAN KOTLIN DO FOR YOU?
▸ Use it in Gradle instead of Groovy
▸ Use it as your main script language in TeamCity
▸ Use it with other JVM languages via JSR-223 (javax.script)
OUTSIDE THE BOX
KOTLIN/NATIVE
▸ Brand new preview: Kotlin/Native
▸ Idea: compiling to machine code
▸ Preview targets: OS X 10.10, Ubuntu, iOS, RaspPi
▸ Goal: Ubiquitous language allowing you to write anything from front end code down to machine-code-level back end services
▸ More: https://blog.jetbrains.com/kotlin/2017/04/kotlinnative-tech-preview-kotlin-without-a-vm/
OUTSIDE THE BOX
COMMUNITY
▸ Slack team with ~6000 people
▸ Jetbrains user group/community program
▸ Multiple books and online courses
▸ Jetbrains KEEP process is open for everyone
▸ 4-5x increase in Kotlin LOC in github over the last year
▸ Lots of interesting frameworks being developed
FINAL THOUGHTS
https://www.flickr.com/photos/brickset/16099265973/
FINAL THOUGHTS
MATURITY AND FUTURE
▸ Kotlin 1.0 was a big step for the language, Kotlin 1.1 added very useful new features
▸ Very mature for a 1.x release (but 6+ years in the making), full of great concepts and idioms, getting better with every release
▸ Tool support is A+
▸ KEEP (Kotlin Evolution and Enhancement Process) helps to steer the language’s future
FINAL THOUGHTS
RESOURCES
▸ Kotlin: http://kotlinlang.org
▸ Anko: https://github.com/Kotlin/anko
▸ kotlinx-coroutines-android:
▸ https://github.com/Kotlin/kotlinx.coroutines/tree/master/ui/kotlinx-coroutines-android
▸ https://github.com/Kotlin/kotlinx.coroutines/blob/master/ui/coroutines-guide-ui.md
▸ AsyncAwait: https://github.com/metalabdesign/AsyncAwait
▸ KEEP: https://github.com/Kotlin/KEEP
FINAL THOUGHTS
GET IN TOUCH
Kai Koenig
Email/iMessage: [email protected]
Work: http://www.ventego-creative.co.nz
Twitter: @AgentK
Telegram: @kaikoenig
Slideshare: /agentk