Kittinun Vantasin
Statically typed language for JVM, Android, and the browser
Created by Jetbrains back in 2010
Open sourced, hit 1.0 released in Feb, 2016
Seamlessly integrate with IntelliJ platform - also Android Studio
Java suffers with backwards & compatibility
Development of Java itself is slow
Java 6 12/2006
Java 7 7/2011
Java 8 3/2014
Java 9 ~3/2017
Flawed Type system
object instanceof T;
T t = (T) object;
T[] array = new T[1];
Low surface area to explore, easier to grasp
Simple, expressive, versatile, interoperable - Pick 4
Compile down to bytecode Java 6, desirable for Android
Safe & Fun
fun main(args: Array<String>) {
val name = args.getOrNull(0) ?: "No name"
println("Hello $name to Kotlin Konference!")
}
Null safety | Enum class | Immutability |
---|---|---|
Type inference & Cast | Properties & Fields | Data class |
Lambda | Extension Functions | High order function |
Type safe builder | Collection API | Kotlin-Java interoperable |
A better way to deal with NPEs
Java | Kotlin | |
---|---|---|
Nullable | T | T? |
Non-null | T | T |
Safe call or handle manually, let to express intent of unwrapping
//cannot compile
val name: String = null
//compile
val nullableName: String? = null
//compile
val nonNullName = "Kittinun" // type is String
fun doSomething(name: String) {
...
}
//cannot compile
doSomething(null)
val anotherNullableString: String? = "nullable"
//compile
doSomething(nonNullName)
val nullableString: String? = null
//safe call
nullableString?.length
//NPE lovers
nullableString!!.length //throw NPE
//unwrapping
val anotherNullableString: String? = "Kittinunf"
anotherNullableString?.let {
//safe as nullable won't enter this block
val length = anotherNullableString.length
println(length) //print 9
}
if (anotherNullableString != null) {
//also safe
println(anotherNullableString.length) //print 9
}
Type safe enum
Contains value that initialized at construction
enum class Orientation(val value: String) {
HORIZONTAL("H") {
override fun intRepresentation() = 1
},
VERTICAL("V") {
override fun intRepresentation() = 2
},
REVERSE_HORIZONTAL("R_H") {
override fun intRepresentation() = -1
};
abstract fun intRepresentation(): Int
}
val h = Orientation.HORIZONTAL
val rh = Orientation.REVERSE_HORIZONTAL
println(h.value) //print "H"
println(rh.intRepresentation()) //print -1
Safe & easily use across code base
Built-in support for mutable & immutable properties/variables
Design with Mutable/Immutable interface
val i: Int = 10
var mi: Int = 5
//cannot compile, i cannot be re-assigned
i = 1000
mi = 1000
//cannot compile, i is immutable and cannot be re-assigned
i += 4
val arr: List<Int> = listOf(1,2,3,4,5)
//cannot compile, add is unresolved reference
arr.add(8)
val marr: MutableList<Int> = mutableListOf(1,2,3,4,5)
marr.add(8) //becomes 1,2,3,4,5,8
No need to explicitly declare type, compiler works that out for you
Smart Cast makes your life much better
val j = 10
//val j: Int = 10
//be explicit as needed
val jj: Int? = 20
val i = powerOfTwo(6)
fun powerOfTwo(i: Int) = i * i
jj?.let {
//smart cast to Int
println(jj.toString())
}
//type is Int?
jj?.toString()
val e: Any = "Hello"
if (e is String) {
//smart cast to String
println(e.length) //print 6
}
Properties in Kotlin is rich in features
Handy delegated properties
enum class Color {
BLUE,
RED,
GREEN,
WHITE;
}
class Car(
val make: String,
val year: Int,
var color: Color,
val vinNumber: String,
var secondHanded: Boolean
)
val jazz = Car("Honda", 2014, Color.WHITE, "aa76efc1", false)
//cannot compile as make is immutable
jazz.make = "Toyota"
jazz.bought()
jazz.secondHanded = true //compile
class Controller {
val computed: String
get() { return "hello" }
var counter = 0
set(value) {
if (value >= 0) field = value
}
lateinit var toBeInit: Foo
}
val c = Controller()
c.computed //return "hello"
c.value = 10 //value = 10
c.value = -1 //value is not set, value is still 10
c.toBeInit //throw exception uninitialized
c.toBeInit = Foo()
c.toBeInit.doSomethingAwesome()
class Foo {
val lazyValue by lazy {
println("lazy will be created once")
Bar()
}
var observedValue by Delegates.observable("not set") {
meta, oldValue, newValue ->
println("From \"$oldValue\" To \"$newValue\"")
}
}
val f = Foo()
f.observedValue = "hello" //print - From "not set" To "hello"
f.observedValue = "world" //print - From "hello" To "world"
Short hand notation for verbose counterpart in Java
class Device(
val type = "Unknown"
val platform = "Android"
)
println(Device()) //print Device@63adf08f
data class DeviceData(
val type = "Unknown"
val platform = "Android"
)
val d1 = DeviceData()
val d2 = DeviceData()
println(d1) //print DeviceData(type=Unknown, platform=Android)
println(DeviceData("Samsung")) //print DeviceData(type=Samsung, platform=Android)
println(d1 == d2) //print true!
val (type, platform) = d1 //type="Unknown", platform="Android"
equals()/hashCode() pair,
toString() of the form "<Class>(c1=..., c2=...)",
componentN() functions corresponding to the properties in their order of declaration,
copy() function
Lambda as a first class type
val powOfTwo = { i: Int -> i * i }
println(powOfTwo(5)) //print 25
class Foo {
var value: String by Delegates.observable("") { meta, oldValue, newValue ->
updateListener?.invoke(newValue)
}
var updateListener: ((String) -> Unit)? = null
}
val f = Foo()
f.value = "v1"
f.updateListener = ::println
f.value = "v2" //print "v2"
f.value = "v3" //print "v3"
Lambda is useful to substitute of usage of interface
You can extend functionality of existing class without interitance
fun Int.fib(): Int {
var last = 0
var prev = 1
var result = 0
for (i in 2..this) {
result = last + prev
last = prev
prev = result
}
return if (this == 1) 1 else result
}
31.fib() //print 1346269
Extremely handy for class that you do not own
Function as first class citizen
fun Int.fib2(): Int {
var (last, prev) = 0 to 1
val s = generateSequence(2) {
val v = it + 1
if (v > this) null else v
}
val result = s.fold((last to prev)) { agg, item ->
val (l, p) = agg
p to l + p
}.second
return if (this == 1) 1 else result
}
31.fib2() //print 1346269
(0..10).filter { it > 5 } // print 6,7,8,9,10
//print 36,49,64,81,100
(0..10).filter { it > 5 }.map { it * it }
//print sum(36,49,64,81,100) 330
(0..10).filter { it > 5 }.map { it * it }.reduce(Int::plus)
Support functional programming concepts
Builder pattern for Free
data class Bar(val i: Int)
class Complicate {
private val children = mutableListOf<Bar>()
companion object {
fun build(init: Complicate.() -> Unit): F {
val c = Complicate()
c.init()
return c
}
}
fun add(b: Bar) {
children.add(b)
}
}
val c = Complicate.build {
add(Bar(1))
add(Bar(2))
add(Bar(3))
} //c.children [Bar(1), Bar(2), Bar(3)]
DSL for class
Rich collection API covers almost every use case
val a = (1..10) + (1..10)
a.all { it > 0 } a.subtract(5..10)
a.any { it % 2 == 0 } a.sortedWith { .... }
a.average() a.sum()
a.distinct() a.take(3)
a.dropWhile { it < 5 } a.toList()/a.toArrayList()
a.filterNot { it > 0 } a.toSet()/a.toHashSet()
a.groupBy { if (it < 3) "<3" else ">3" } a.toCollection(MyAwesomeCollection())
a.intersect(2..4) a.toLinkList()
a.joinToString("-") a.toMap({ if (it % 2 == 0) "even" else "odd" },
{ it.toString })
a.max(), a.min() a.map { it to it.toString() }.unzip()
a.none { it < 1 } a.withIndex()
a.reversed() a.union(10..20)
a.sortedBy { it % 3 == 0 } a.zip(10..20)
Backend ? - Yes, ktor (https://github.com/Kotlin/ktor)
Frontend ? - Kotlin supports Javascript, see for youself at web-demo (http://try.kotlinlang.org/#/Examples/Canvas/Fancy%20lines/Fancy%20lines.kt)
Desktop ? - It is!, take a look at TornadoFx (https://github.com/edvin/tornadofx)
If not, go see it yourself https://kotlinlang.org/
Try online at http://try.kotlinlang.org/
Expressiveness makes Android development pleasant
Extension function brings Android dev to the whole new level
Interop with Java makes you feel "at home"
If you know Java, Kotlin is half way there
Then, you are 3 fingers away from Kotlin?
Cmd + Shift + a
Getting started by install Kotlin IDE plugin
Java
Kotlin
javac
kotlinc
bytecode
Android
Dalvik
ART
import kotlinx.android.synthetic.main.activity_main.*
class MyAwesomeActivity : Activity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_my_awesome)
textView.text = "Hello Kotlin for Android"
}
}
<TextView
android:id="@+id/textView"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
tools:text="John Appleseed"
/>
class MyAwesomeActivity : Activity() {
fun handleSuccess() {
toast("Success!")
//long
toast("Successsssss!", Toast.LENGTH_LONG)
logD("hello")
}
}
fun Context.toast(msg: String,
length: Int = Toast.LENGTH_SHORT) {
Toast.makeText(this, msg, length).show()
}
fun logD(msg: Any, tag: String = "LOG") {
Log.d(tag, msg.toString())
}
fun Realm.transaction(call: (Realm) -> Unit) {
beginTransaction()
call(this)
commitTransaction()
}
open class Dog(open var name: String,
open var age: Int,
open var breed: String) : RealmObject
val dog = Dog("Albert", 3, "Shizu")
val realm = Realm.getDefaultInstance()
realm.transaction {
it.copyToRealm(dog)
}
val url = URL("http://www.google.com")
val executor = Executors.newScheduledThreadPool(4)
override fun onCreate(savedInstanceState: Bundle?) {
dispatch(executor) {
url.readText()
}
}
class Async<T>(val weakRef: WeakReference<T>)
fun <T> T.dispatch(executorService: ExecutorService,
block: Async<T>.() -> Unit)
: Future<Unit> {
val a = Async(WeakReference(this))
return executorService.submit<Unit> { a.block() }
}
operator fun ViewGroup.get(index: Int): View
= getChildAt(index)
val container = getContainer()
val childAtTwo = container[2]
override fun onBindViewHolder(holder: ViewHolder, index: Int) {
val car = items[index]
with(car) {
Glide.with(holder.itemView.context).load(coverPhotoUrl)
itemView.descTextView.text = desc
itemView.setOnClickListener {
toast("ItemClick")
}
}
}
//singleton
object GsonManager {
fun register() {
}
fun addDateTimeStampFormat(format: String) {
}
}
class MyAwesomeApplication : Application() {
override fun onCreate() {
GsonManager.register(...)
}
}
//delegates
class MyAwesomeListActivity : Activity() {
private var items: List<Model> by Delegates.observable(listOf()) {
meta, oldValue, newValue ->
adapter.notifyDataSetChanged()
}
val adapter = MyAwesomeAdapter()
fun onSuccess(models: List<Model>) {
items = models
}
}
val name: String? = "Andy"
val address: String? = null
name?.let {
//name has been smart-casted to String
println(name.length) //print 4
}
address?.let {
//this block is skipped due to safe call (?.)
}
data class Person(name: String, age: Int, address: String)
fun constructPerson(name: String, age: Int, address: String): Person {
return Person().apply {
name = "Wipoo"
age = 24
address = "68 Soi Phahonyothin 6"
}
}
//use makes closable operation easy to reason, and safe
Files.newOutputStream(path).use {
it.write("foo")
it.write("bar")
}
//resource is closed properly
Utils: taskworld/KxAndroid
Networking: kittinunf/Fuel
Functional patterns: kittinunf/Result
Reactive Components: kittinunf/ReactiveAndroid
SharedPreference: verachadw/Kreference
Cache: kittinunf/Fuse