exploring koltin on android

83
Exploring Kotlin on Android by Deepanshu & Malcolm

Upload: deepanshu-madan

Post on 12-Apr-2017

189 views

Category:

Mobile


0 download

TRANSCRIPT

Exploring Kotlin on Android

by Deepanshu & Malcolm

What is Kotlin?• Statically typed programming language

  • JVM-based language developed by JetBrains

• From industry not academia

• 100% inter-operable with the Java language

Open Source - https://github.com/JetBrains/kotlin

Why Kotlin

Concise - Drastically reduce the amount of boilerplate code you need to write.

Safe - Avoid entire classes of errors such as null pointer exceptions.

Versatile - Build server-side applications, Android apps or front-end code running in the browser.

Kotlin in Expedia Android

Kotlin code is at 23% and rising.

Refactoring started mid 2015.

Hotels, Packages, Flights, Services and MockWebServer & tests classes are 90% in Kotlin

Cars/LX/Legacy tablet code still in java.

Other teams in Expedia are experimenting with Kotlin in the web.

Features• Null type and safety

• Lambdas

• Optional params

• Data classes

• Extension functions

• Delegates

• Smart Casting

Not so good about Kotlin!

• Tools support

• Compile time

Kotlin Roadmap for Android

• Incremental compilation

• support for Jack and Jill toolchain

• Instant run works for cold swaps right now not for hot swaps

• Lint checks in Kotlin 1.0.2

Kotlin syntax 101

Defining Functions

fun sum(a: Int, b: Int): Int { return a + b }

Defining Functions

fun sum(a: Int, b: Int): Int { return a + b }

fun sum(a: Int, b: Int) = a + b

fun printSum(a: Int, b: Int): Unit { print(a + b)}

Defining Functions

fun printSum(a: Int, b: Int): Unit { print(a + b)}

fun printSum(a: Int, b: Int) { print(a + b)}

Defining Functions

fun doSomething(): Int { val a: Int = 1 val b = 1 val c: Int c = a + b return c}

Defining local variables

fun doSomething(): Int { val a: Int = 1 var b = 1 val c: Int

b += 5 c = a + b return c}

Defining local variables

Using conditional expressions

fun doSomething(a: Int, b: Int): Int { if (a > b) return a else return b }

Using conditional expressions

fun doSomething(a: Int, b: Int): Int { if (a > b) return a else return b}

fun doSomething(a: Int, b: Int) = if (a > b) a else b

Using a for loopfun doSomething(args: Array<String>) { for (arg in args) print(arg)}

Using a for loopfun doSomething(args: Array<String>) { for (arg in args) print(arg)}

fun doSomething(args: Array<String>) { for (i in args.indices) print(args[i])}

Using when expressionfun doSomething(obj: Any) { when (obj) { 1 -> print("One") "Hello" -> print("Kotlin") is Long -> print("Long") !is String -> print(“!string") else -> print("Unknown") }}

Using rangesfun doSomething(x: Int, y: Int) { if (x in 1..y - 1) print("In") for (x in 1..5) print(“N")

if (x !in 1..y - 1) print(“In")

}

Using rangesfun doSomething(x: Int, y: Int) { if (x in 1..y - 1) print("In") for (x in 1..5) print("N") if (x !in 1..y - 1) print(“In") }

Using rangesfun doSomething(x: Int, y: Int) { if (x in 1..y - 1) print("In") for (x in 1..5) print("N") if (x !in 1..y - 1) print(“In") }

Class in Javapublic class SomeClass { private String variable; private final String defaultVar; SomeClass(String variable) { this.variable = variable; this.defaultVar = "Java"; } SomeClass(String variable, String defaultVar) { this.variable = variable; this.defaultVar = defaultVar; } }

// Use new SomeClass("Kotlin", "Java"); new SomeClass("Kotlin");

Class in Kotlin

class SomeClass(var variable: String, val defaultValue: String = "Java") {}

// UseSomeClass("Kotlin", "Java") SomeClass("Kotlin")

Lambdas

fun doSomething() { val items = ArrayList<String>() items.sortBy { item -> item.length } }

Lambdas

fun doSomething() { val items = ArrayList<String>() items.sortBy { it.length } }

Lambdas//In Kotlin

fun doSomething() { val items = ArrayList<String>() items.sortBy { it.length } }

//In Java

ArrayList<String> items = new ArrayList(); Collections.sort(items, new Comparator<String>(){ @Override public int compare(String s1, String s2) { return s1.length() - s2.length(); } });

button.setOnClickListener(new View.OnClickListener(){ public void onClick(View v) { Toast.makeText(this.MainActivity, "Button", Toast.LENGTH_LONG).show(); } });

In Java

button.setOnClickListener(object: View.OnClickListener { override fun onClick(view: View): Unit { Toast.makeText(context, "Button", Toast.LENGTH_LONG).show() }})

In Kotlin

button.setOnClickListener { view -> Toast.makeText(this, "Button", Toast.LENGTH_LONG).show()}

In Kotlin

button.setOnClickListener { Toast.makeText(this, "Button", Toast.LENGTH_LONG).show()}

In Kotlin

//In Kotlin

button.setOnClickListener { Toast.makeText(this, "Button", Toast.LENGTH_LONG).show()}

//In Java

button.setOnClickListener(new View.OnClickListener(){ public void onClick(View v) { Toast.makeText(this.MainActivity, "Button", Toast.LENGTH_LONG).show(); } });

class Vehicle(var value: String) {} fun doSomething() { val type = Vehicle("Car") println("${type.value}")}

class Vehicle(var value: String) {} fun doSomething() { val type = Vehicle("Car") println("${type.value}")}

class Vehicle(var value: String) {} fun doSomething() { val type = Vehicle("Car") println("${type.value}") }

class Vehicle(var value: String) { fun print() = println("$value") } fun doSomething() { val type = Vehicle("Car") type.print()}

class Vehicle(var value: String) { fun print() = println("$value") } fun doSomething() { val type = Vehicle("Car") type.print()}

class Vehicle(var value: String) { fun print() = println("$value")} fun doSomething() { val types = listOf( Vehicle("Car"), Vehicle("Truck"), Vehicle("Bike")) }

class Vehicle(var value: String) { fun print() = println("$value")} fun doSomething() { val types = listOf( Vehicle("Car"), Vehicle("Truck"), Vehicle("Bike")) for (type in types) { type.print() }}

class Vehicle(var value: String) { fun print() = println("$value")} fun doSomething() { val types = listOf( Vehicle("Car"), Vehicle("Truck"), Vehicle("Bike")) types.forEach { type -> type.print() } }

class Vehicle(var value: String) { fun print() = println("$value")} fun doSomething() { val types = listOf( Vehicle("Car"), Vehicle("Truck"), Vehicle("Bike")) types.forEach { it.print() } }

class Vehicle(var value: String) { fun print() = println("$value") } fun doSomething() { val types = listOf( Vehicle("Car"), Vehicle("Truck"), Vehicle("Bike")) types.forEach { it.print() } }

class Vehicle(var value: String) { fun print() = println("$value") } fun doSomething() { listOf( Vehicle("Car"), Vehicle("Truck"), Vehicle("Bike") ).forEach { it.print() } }

open class Vehicle(var value: String) { fun print() = println("$value")} class Car(var type: String) : Vehicle(type){} fun doSomething() { listOf( Car("Car"), Vehicle("Truck"), Vehicle("Bike") ).forEach{it.print()} }

open class Vehicle(var value: String) { fun print() = println("$value") } class Car(var type: String) : Vehicle(type){} fun doSomething() { listOf( Car("Car"), Vehicle("Truck"), Vehicle("Bike") ).forEach{it.print()} }

open class Vehicle(var value: String) { open fun print() = println("$value") } class Car(var type: String) : Vehicle(type){ override fun print() = println("Yo, Its a $value") } fun doSomething() { listOf( Car("Car"), Vehicle("Truck"), Vehicle("Bike") ).forEach{it.print()} }

open class Vehicle(var value: String) { open fun print() = println("$value") } class Car(var type: String) : Vehicle(type){ override fun print() = println("Yo, Its a $value") } fun doSomething() { listOf( Car("Car"), Vehicle("Truck"), Vehicle("Bike") ).forEach{it.print()} }

Nullability I call it my billion-dollar mistake. It was the invention of the null reference …. has led to innumerable errors, vulnerabilities, and system crashes, which have probably caused a billion dollars of pain and damage in the last forty years.

-Sir Charles Antony Richard Hoare

Null and type safety

fun doSomething() { var a: String = "abc" a = null // compilation error}

fun doSomething() { var a: String = "abc" a = null // compilation error var b: String? = "abc" b = null // ok}

Null and type safety

fun doSomething() { var a: String = "abc" var b: String? = "abc" b = null // ok val l = a.length}

Null and type safety

fun doSomething() { var a: String = "abc" var b: String? = "abc" b = null // ok val l1 = a.length val l2 = b.length // error:variable 'b' can be null}

Null and type safety

Checking for null in conditions

fun doSomething() { var a: String = "abc" var b: String? = "abc" b = null // ok val l1 = a.length val l = if (b != null) b.length else -1 }

Safe Calls

fun doSomething() { var a: String = "abc" var b: String? = "abc" b = null // ok val l1 = a.length val l2 = b?.length }

Elvis Operator

fun doSomething() { var a: String = "abc" var b: String? = "abc" b = null // ok val l1 = a.length val l = if (b != null) b.length else -1 }

fun doSomething() { var a: String = "abc" var b: String? = "abc" b = null // ok val l1 = a.length val l2 = b?.length ?: -1 }

if (originLocation != null) { if (originLocation.hierarchyInfo != null) { if (originLocation.hierarchyInfo.airport != null) { if (originLocation.hierarchyInfo.airport.airportCode != null) { departureAirportCode = originLocation.hierarchyInfo.airport.airportCode; } } } } else { departureAirportCode = ""; }

val departureAirportCode = originLocation?.hierarchyInfo?.airport?.airportCode ?: ""

if (originLocation != null) { if (originLocation.hierarchyInfo != null) { if (originLocation.hierarchyInfo.airport != null) { if (originLocation.hierarchyInfo.airport.airportCode != null) { departureAirportCode = originLocation.hierarchyInfo.airport.airportCode; } } } } else { departureAirportCode = "";}

The !! Operator

fun doSomething() { var a: String = "abc" var b: String? = "abc" b = null // ok val l1 = a.length val l2 = b!!.length }

Optional-params //In Java

void foo(int p1, boolean p2) { } void foo(int p1) { foo(1, false); } void foo() { foo(1, false); }

Optional-params

//In Kotlin fun foo(p1: Int = 1, p2: Boolean = false) {}

foo()foo(3)

Data class

data class Traveler(val name: String?, val age: Int)

Automatically implements:

equals()/hashCode()toString() of the form "User(name=John, age=42)", copy() function

val testTravlerJohn = Traveler(“John”, 42) val testTravlerJane = Traveler(“Jane”, 41)

assertEquals(testTravlerJohn, testTravlerJane)

Data class in Testing

public class Traveler(String name, Int age)

@Overridepublic boolean equals(Object o) { if (this == o) { return true; } if (!(o instanceof Traveler)) { return false; } Traveler traveler = (Traveler) o; if (name != name) { return false; } if (age != traveler.age) { return false; }

return true; }

Extension Functions//In Java public class StrUtils { public static String encodeString(String str) { return str.replaceAll(" ", "_") } }

String text = "Hello World"; System.out.println(StrUtils.encodeString(text));

Extension Functions//In Kotlin fun String.encodeSpaces(): String { return this.replaceAll(" ", "_") }

val text = "hello world"println(text.encodeSpaces())

val list = listOf("E", "H", "Hello", "Hi", "I") val map = mapOf(Pair("A", 1), Pair("B", 2), Pair("C", 3)) list.forEach { s -> s.length}

"E, H, Hello, Hi, I"

Kotlin Collections Extensions

val list = listOf("E", "H", "Hello", "Hi", "I")val map = mapOf(Pair("A", 1), Pair("B", 2), Pair("C", 3))var count = 0 list.forEachIndexed { i, s -> count += i s.length} "E, H, Hello, Hi, I"

val list = listOf("E", "H", "Hello", "Hi", "I")val map = mapOf(Pair("A", 1), Pair("B", 2), Pair("C", 3))list.filter { s -> s.contains("H") } "H, Hello, Hi"

val list = listOf("E", "H", "Hello", "Hi", "I")val map = mapOf(Pair("A", 1), Pair("B", 2), Pair("C", 3))list.first { s -> s.startsWith("He") } "Hello"

val list = listOf("E", "H", "Hello", "Hi", "I")val map = mapOf(Pair("A", 1), Pair("B", 2), Pair("C", 3))map.forEach { entry -> entry.value } "A, B, C"

//In Java final int[] sponsoredIndexes = { 0, 50, 51 }; ArrayList<Property> sponsored = new ArrayList<Property>(); ListIterator<Property> it = properties.listIterator(); while (it.hasNext()) { Property prop = it.next(); if (prop.isSponsored()) { it.remove(); sponsored.add(prop); } } for (int i = 0; i < sponsored.size() && i < sponsoredIndexes.length; i++) { if (sponsoredIndexes[i] <= properties.size()) { properties.add(sponsoredIndexes[i], sponsored.get(i)); } }

//In Kotlin fun putSponsoredItemsInCorrectPlaces(hotelList: List<Hotel>): List<Hotel> { val (sponsored, nonSponsored) = hotelList.partition { it.isSponsoredListing } val firstChunk = sponsored.take(1) val secondChunk = nonSponsored.take(49) val thirdChunk = sponsored.drop(1) val rest = nonSponsored.drop(49) return firstChunk + secondChunk + thirdChunk + rest}

//In Java Observable.just("Hello World") .map(new Func1<String, Object>() { @Override public Object call(String s) { return s + "!"; } }) .subscribe(new Subscriber<String>() { @Override public void onCompleted() { //Completion } @Override public void onError(final Throwable e) { //TODO : Handle error here } @Override public void onNext(final String s) { Log.e("Output",s); } });

Reative Java

Kotlin & Reactive

Observable.just("Hello World") .map { it.plus("!") }

.subscribe { println("Output:" + it); }

Flow sensitive typing//In Java

@Overridepublic void onBindViewHolder(RecyclerView.ViewHolder holder, int position) { if (holder.getItemViewType() != LOADING_VIEW) { ((ViewHolder) holder).updateHotel(); } else if (holder.getItemViewType() != HOTEL_VIEW) { ((LoadingViewHolder) holder).loadView(); } }

Flow sensitive typing//In Kotlin

override fun onBindViewHolder(holder: RecyclerView.ViewHolder, position: Int) { when (holder) { is HotelCellViewHolder-> holder.updateHotel() is LoadingViewHolder -> holder.loadView() }}

Delegatesvar p: String by Delegate()

Properties using the delegate expression will have its get() and set() methods delegated to the expression.

* lazy

* observable

* notNull

val view: Presenter by lazy { var view = stub.inflate() as Presenter view}

Delegates.lazy

Delegates.notNull

class Foo { var bar : Bar by Delegates.notNull() // type is now Bar init { val s = bar // Using before setting throws an IllegalStateException! bar = Bar() }}

Delegates.observable

val text: String by Delegates.observable("") { prop, old, new -> println("$old -> $new") }

Custom delegate(KotterKnife)

fun <T : View> ViewGroup.bindView(id: Int): ReadOnlyProperty<Any, T> = ViewBinding(this, id)

private class ViewBinding<T : View>(val source: Any, val id: Int) : ReadOnlyProperty<Any, T> { private val lazy = Lazy<T>() override fun getValue(thisRef: Any, property: KProperty<*>): T = lazy.get { findView<T>(source, id) ?: throw IllegalStateException("View ID $id for '${property.name}' not found.") } }

val mapView: MapView by bindView(R.id.map_view)

Questions?