first few months with kotlin - introduction through android examples

76
First few months with otlin Introduction through Android examples Nebojša Vukšić

Upload: nebojsa-vuksic

Post on 07-Apr-2017

71 views

Category:

Education


1 download

TRANSCRIPT

First few months with otlinIntroduction through Android examples

Nebojša Vukšić

What is Kotlin?

What is Kotlin?

● statically-typed object oriented programming language● targeting JVM, Android and JavaScript● fully interoperable with Java● third party library● has excellent IDE support

Kotlin history

● developed by JetBrains● unveiled to public in 2011.(development started in 2012.)● 1.0 first stable version (February 2016.)● current version 1.1 RC

Why do we need Kotlin?

Problems that we have:

Why do we need Kotlin?

● Java○ is too verbose○ burden of previous versions○ Null Pointer Exception

issues○ util “hell”

● Android○ we need inheritance for

almost everything○ api ceremony○ nullability ○ lack of Java 8 features

(lambdas, stream api, method reference...)

Basics

Variablesval avenger: String = “Tony Stark” //constants

avenger = “Ultron” // compile error

var age : Int = 18 //variable

age = 20 // compiles since age is mutable

var number = 20 // Int type is inferred

Nullability

text.length // compiler error

var text: String? = null // This can be null or not-null

Nullabilityvar name: String = null // compile error

text?.length // compiles ⇔ if ( text != null) {

text.length // smart casted to not-nullable type

}

name.length // this is ok since type is not nullable

Nullability

val s: String? = "This can be null or not-null"

val length = s!!.length

Making NPE explicit

Nullability

val s: String? = "This can be null or not-null"

val length = s!!.length

Making NPE explicit

Functionsfun add(a: Int, b: Int): Int {

return a + b

}

Calling functions:

add(1, 3)

log(1, “Num is”)

//“Num is 1”

fun log(num: Int, msg: String): Unit {

println(“$msg $num”)

}

Functionsfun add(a: Int, b: Int): Int = a + b

fun log(num: Int, msg: String): Unit

= println(“$msg $num”)

Calling functions:

add(1, 3)

log(1, “Num is”)

//“Num is 1”

Functionsfun add(a: Int, b: Int) = a + b

fun log(num: Int, msg: String)

= println(“$msg $num”)

Calling functions:

add(1, 3)

log(1, “Num is”)

//“Num is 1”

Functionsfun add(a: Int = 0, b: Int = 0) = a + b

fun log(num: Int, msg: String = “Num is”)

= println(“$msg $num”)

add() //returns 0

add(1) //returns 1

add(b = 1) //returns 1

log(1) // “Num is 1”

log(msg = “Its ”, num = 2)

// “Its 2”

Functions

fun `should equal 4`() {

assertEquals(4, 2 + 2)

}

`should equal 4`()

Kotlin does classes differently...

Classes and data classes @Override public boolean equals(Object o) { if (this == o) return true; if (o == null || getClass() != o.getClass()) return false;

User user = (User) o;

if (age != user.age) return false; if (!name.equals(user.name)) return false; return email.equals(user.email); }

@Override public int hashCode() { int result = name.hashCode(); result = 31 * result + email.hashCode(); result = 31 * result + age; return result; }}

class User { private final String name; private final String email; private final int age;

User(String name, String email, int age) { this.name = name; this.email = email; this.age = age; } public String getName() { return name; } public String getEmail() { return email; } public int getAge() { return age; }

@Override public String toString() { return "User{ name='" + name + '\'' + ", email='" + email + '\'' + ", age=" + age + '}'; }

Classes and data classes @Override public boolean equals(Object o) { if (this == o) return true; if (o == null || getClass() != o.getClass()) return false;

User user = (User) o;

if (age != user.age) return false; if (!name.equals(user.name)) return false; return email.equals(user.email); }

@Override public int hashCode() { int result = name.hashCode(); result = 31 * result + email.hashCode(); result = 31 * result + age; return result; }}

class User { private final String name; private final String email; private final int age;

User(String name, String email, int age) { this.name = name; this.email = email; this.age = age; } public String getName() { return name; } public String getEmail() { return email; } public int getAge() { return age; }

@Override public String toString() { return "User{ name='" + name + '\'' + ", email='" + email + '\'' + ", age=" + age + '}'; }

Classes and data classes @Override public boolean equals(Object o) { if (this == o) return true; if (o == null || getClass() != o.getClass()) return false;

User user = (User) o;

if (age != user.age) return false; if (!name.equals(user.name)) return false; return email.equals(user.email); }

@Override public int hashCode() { int result = name.hashCode(); result = 31 * result + email.hashCode(); result = 31 * result + age; return result; }

class User ( val name: String, val email: String, val age: Int)

@Override public String toString() { return "User{ name='" + name + '\'' + ", email='" + email + '\'' + ", age=" + age + '}'; }

Classes and data classesdata class User ( val name: String, val email: String, val age: Int)

val user = User("John Smith", "[email protected]", 24)

val newUser = user.copy(name = "Sam")

//newUser == User("Sam", "[email protected]", 24)

val (name, email, age) = newUser

Data classesdata class User (

val name: String = "John Smith",

val email: String = "[email protected]",

val age: Int = 24

)

val user = User ()

// user == User("John Smith", "[email protected]", 24)

Classes and data classes

data class User( val name: String, val email: String, val age: Int)

● Kotlin classes are final by default● we need to annotate class as open, if we want to inherit them● data classes can’t be inherited

class limitations:

Classes and data classes

open class User( val name: String, val email: String, val age: Int)

● Kotlin classes are final by default● we need to annotate class as open, if we want to inherit them ● data classes can’t be inherited

class limitations:

Extension functions and properties

Extension functionsfun String.swapSpacesForUnderscore(): String = this.replace(" ", "_")

"This is random message!".swapSpacesForUnderscore()

// returns "This_is_random_message!"

Compiles to:

public static final String swapSpacesForUnderscore(@NotNull String $receiver) { Intrinsics.checkParameterIsNotNull($receiver, "$receiver"); return $receiver.replace(," ", "_");}

Extension functionspackage com.app.android.util

fun String.swapSpacesForUnderscore(): String = this.replace(" ", "_")

package com.app.android.main

"Random text!".swapSpacesForUnderscore()

Extension functionspackage com.app.android.util

fun String.swapSpacesForUnderscore(): String = this.replace(" ", "_")

package com.app.android.main

import com.app.android.util.swapSpacesForUnderscore

"Random text!".swapSpacesForUnderscore()

Extension properties

val Array<String>.lastElement: String get() = this[size - 1]

val names: Array<String> = arrayOf("John", "Will", "Emma", "Peter")val name = names.lastElement //returns "Peter"

Compiles to:

public static final String getLastElement(@NotNull String[] $receiver) { Intrinsics.checkParameterIsNotNull($receiver, "$receiver"); return $receiver[$receiver.length - 1];}

Extension properties

val <T> Array<T>.lastElement: T get() = this[size - 1]

val names: Array<String> = arrayOf("John", "Will", "Emma", "Peter")val name = names.lastElement //returns "Peter"

Compiles to:

public static final Object getLastElement(@NotNull Object[] $receiver) { Intrinsics.checkParameterIsNotNull($receiver, "$receiver"); return $receiver[$receiver.length - 1];}

Extension properties

val <T> Array<T>.lastElement: T get() = this[size - 1]

val nums: Array<Int> = arrayOf(1, 2, 3, 4)val num = nums.lastElement //returns 4

Compiles to:

public static final Object getLastElement(@NotNull Object[] $receiver) { Intrinsics.checkParameterIsNotNull($receiver, "$receiver"); return $receiver[$receiver.length - 1];}

Android extensions plugin

no more findViewById()

Android extensions pluginclass MainActivity : AppCompatActivity() {

override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) setContentView(R.layout.activity_main)

val toolbar: Toolbar = findViewById(R.id.toolbar) as Toolbar setSupportActionBar(toolbar)

val fab: FloatingActionButton = findViewById(R.id.fab) as FloatingActionButton fab.setOnClickListener { Toast.makeText(this, "Hello!", Toast.LENGTH_SHORT).show() }}

}

Android extensions pluginclass MainActivity : AppCompatActivity() {

@BindView(R.id.toolbar) Toolbar toolbar;@BindView(R.id.fab) FloatingActionButton fab;

override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) setContentView(R.layout.activity_main) ButterKnife.bind(this)

setSupportActionBar(toolbar)

fab.setOnClickListener { Toast.makeText(this, "Hello!", Toast.LENGTH_SHORT).show() }}

}

Android extensions pluginimport kotlinx.android.synthetic.main.activity_main.toolbarimport kotlinx.android.synthetic.main.activity_main.fab

class MainActivity : AppCompatActivity() {

override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) setContentView(R.layout.activity_main)

setSupportActionBar(toolbar)

fab.setOnClickListener { Toast.makeText(this, "Hello!", Toast.LENGTH_SHORT).show() }}

}

Android extensions pluginimport kotlinx.android.synthetic.main.activity_main.toolbarimport kotlinx.android.synthetic.main.activity_main.fab

class MainActivity : AppCompatActivity() {

override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) setContentView(R.layout.activity_main)

setSupportActionBar(toolbar)

fab.setOnClickListener { Toast.makeText(this, "Hello!", Toast.LENGTH_SHORT).show() }}

}

Android extensions pluginimport kotlinx.android.synthetic.main.activity_main.toolbarimport kotlinx.android.synthetic.main.activity_main.loging_btn_registration_fragment

class MainActivity : AppCompatActivity() {

override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) setContentView(R.layout.activity_main)

setSupportActionBar(toolbar)

loging_btn_registration_fragment.setOnClickListener { Toast.makeText(this, "Hello!", Toast.LENGTH_SHORT).show() }}

}

Android extensions pluginimport kotlinx.android.synthetic.main.activity_main.toolbarimport kotlinx.android.synthetic.main.activity_main.loging_btn_registration_fragment as login

class MainActivity : AppCompatActivity() {

override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) setContentView(R.layout.activity_main)

setSupportActionBar(toolbar) login.setOnClickListener { Toast.makeText(this, "Hello!", Toast.LENGTH_SHORT).show()}}

}

Function expressions

Function expressions

val add: (Int, Int) -> Int = { x,y -> x+y }add(1,2)

val validator: (String) -> Boolean ={ value -> value.contains("@") }validator("[email protected]")

● function expressions are blocks of code which we can instantiate(represent as type)

Function expressions

val add: (Int, Int) -> Int = { x,y -> x+y }add(1,2)

● function expressions are blocks of code which we can instantiate(represent as type)

val validator: (String) -> Boolean ={ it.contains("@") }validator("[email protected]")

Function expressions

val add: (Int, Int) -> Int = { x,y -> x+y }add(1,2)

val validator: (String) -> Boolean ={ it.contains("@") }validator("[email protected]") mailTextview.validateWith{ validator("[email protected]") }

● function expressions are blocks of code which we can instantiate(represent as type)

Higher order functions

Higher order functionsfun <T> List<T>.filter(predicate: (T) -> Boolean): List<T>{

}

Higher order functionsfun <T> List<T>.filter(predicate: (T) -> Boolean): List<T>{ val items = ArrayList<T>()

return items}

Higher order functionsfun <T> List<T>.filter(predicate: (T) -> Boolean): List<T>{ val items = ArrayList<T>() for (item in this) {

} return items}

Higher order functionsfun <T> List<T>.filter(predicate: (T) -> Boolean): List<T>{ val items = ArrayList<T>() for (item in this) { if (predicate(item)) { items.add(item) } } return items}

val cars = listOf("BMW", "Fiat", "Mercedes", "KIA", "Ford")val filteredCars = cars.filter ({ it.startsWith("F") })

// filteredCars == listOf("Fiat", "Ford")

Higher order functionsfun <T> List<T>.filter(predicate: (T) -> Boolean): List<T>{ val items = ArrayList<T>() for (item in this) { if (predicate(item)) { items.add(item) } } return items}

val cars = listOf("BMW", "Fiat", "Mercedes", "KIA", "Ford")val filteredCars = cars.filter { it.startsWith("F") }

// filteredCars == listOf("Fiat", "Ford")

Quick overview still not over

Quick overview

● Extension functions - adds functionality to types without overriding existing methods

● Function expressions - undeclared function body used as an expression

● Higher order function - function that accepts function or returns function

Combination of this three makes powerful combo

fun saveUser(user: User) { val editor = sharedPref.edit()

editor.putString(ACCESS_TOKEN, user.token) editor.putString(USER_EMAIL, user.email)

editor.commit()

}

Extension / Higher order function expression combo

fun SharedPreferences.edit(editor : SharedPreferences.Editor, func: () -> Unit) { func() editor.commit()}

Extension / Higher order function expression combofun saveUser(user: User) {

val editor = sharedPref.edit()sharedPref.edit(editor) {

editor.putString(ACCESS_TOKEN, user.token) editor.putString(USER_EMAIL, user.email) }

}

fun SharedPreferences.edit(editor : SharedPreferences.Editor, func: () -> Unit) { func() editor.commit()}

fun saveUser(user: User) { val editor = sharedPref.edit()sharedPref.edit(editor) {

editor.putString(ACCESS_TOKEN, user.token) editor.putString(USER_EMAIL, user.email)

}}

Extension / Higher order function expression combo

fun SharedPreferences.edit(editor : SharedPreferences.Editor, func: () -> Unit) { func() editor.commit()}

fun saveUser(user: User) { sharedPref.edit {

editor.putString(ACCESS_TOKEN, user.token) editor.putString(USER_EMAIL, user.email)

}}

Extension / Higher order function expression combo

fun SharedPreferences.edit(func: () -> Unit) {val editor = edit()

func() editor.commit()}

fun saveUser(user: User) { sharedPref.edit {

it.putString(ACCESS_TOKEN, user.token) it.putString(USER_EMAIL, user.email)

}}

Extension / Higher order function expression combo

fun SharedPreferences.edit(func: (SharedPreferences.Editor) -> Unit) {val editor = edit()

func(editor) editor.commit()}

fun saveUser(user: User) { sharedPref.edit {

it.putString(ACCESS_TOKEN, user.token) it.putString(USER_EMAIL, user.email)

}}

Extension / Higher order function expression combo

fun SharedPreferences.edit(func: (SharedPreferences.Editor) -> Unit) {val editor = edit()

func(editor) editor.commit()}

fun saveUser(user: User) { sharedPref.edit {

it.putString(ACCESS_TOKEN, user.token) it.putString(USER_EMAIL, user.email)

}}

Extension / Higher order function expression combo

fun SharedPreferences.edit(func: SharedPreferences.Editor.() -> Unit)) {val editor = edit()

func(editor) editor.commit()}

fun saveUser(user: User) { sharedPref.edit {

it.putString(ACCESS_TOKEN, user.token) it.putString(USER_EMAIL, user.email)

}}

Extension / Higher order function expression combo

fun SharedPreferences.edit(func: SharedPreferences.Editor.() -> Unit)) {val editor = edit()

editor.func() editor.commit()}

fun saveUser(user: User) { sharedPref.edit {

putString(ACCESS_TOKEN, user.token) putString(USER_EMAIL, user.email)

}}

Extension / Higher order function expression combo

fun SharedPreferences.edit(func: SharedPreferences.Editor.() -> Unit) {val editor = edit()

editor.func() editor.commit()}

Extension / Higher order function expression combo

fun SharedPreferences.Editor.put(pair: Pair<String, Any>) { val key = pair.first val value = pair.second when(value) {

is String -> putString(key, value) is Int -> putInt(key, value) is Boolean -> putBoolean(key, value) is Float -> putFloat(key, value) is Long -> putLong(key, value) else -> error(“Only primitive types are supported”)

}}

fun saveUser(user: User) { sharedPref.edit {

putString(ACCESS_TOKEN, user.token) putString(USER_EMAIL, user.email)

}}

Extension / Higher order function expression combo

fun SharedPreferences.edit(func: SharedPreferences.Editor.() -> Unit)) {val editor = edit()

editor.func() editor.commit()}

Extension / Higher order function expression combofun saveUser(user: User) {

sharedPref.edit {

put(Pair(ACCESS_TOKEN, user.token)) put(Pair(USER_EMAIL, user.email))

}

}

fun SharedPreferences.edit(func: SharedPreferences.Editor.() -> Unit) {val editor = edit()

editor.func() editor.commit()}

fun saveUser(user: User) {

sharedPref.edit {

put(ACCESS_TOKEN to user.token) put(USER_EMAIL to user.email)

}

}

Extension / Higher order function expression combo

fun SharedPreferences.edit(func: SharedPreferences.Editor.() -> Unit) {val editor = edit()

editor.func() editor.commit()}

fun saveUser(user: User) {

sharedPref.edit {

put(ACCESS_TOKEN to user.token) put(USER_EMAIL to user.email)

}

}

Extension / Higher order function expression combo

fun SharedPreferences.edit(func: SharedPreferences.Editor.() -> Unit) {val editor = edit()

editor.func() editor.commit()}

public infix fun <A, B> A.to(that: B): Pair<A, B> = Pair(this, that)

fun saveUser(user: User) {

sharedPref.edit {

put(ACCESS_TOKEN to user.token) put(USER_EMAIL to user.email)

}

}

Extension / Higher order function expression combo

inline fun SharedPreferences.edit(func: SharedPreferences.Editor.() -> Unit) {val editor = edit()

editor.func() editor.commit()}

Extension / Higher order function expression comboFrom this:

fun saveUser(user: User) {val editor = sharedPref.edit()

editor.putString(ACCESS_TOKEN, user.token) editor.putString(USER_EMAIL, user.email)

editor.commit()

}

To this:

inline fun saveUser(user: User) { sharedPref.edit {

put(ACCESS_TOKEN to user.token) put(USER_EMAIL to user.email)

}

}

Summary don’t worry, next slide is the last

Summary● immutable and mutable variables● nullability● functions(default values and named arguments)● classes and data classes● extension functions and properties● function expression● higher order functions● ultra mega giga combo of three above concepts● use inline modifier

Resources● Official Kotlin documentation● Official Kotlin Github● Anko ● Kotlin koans● Awesome Kotlin – collection of materials● Slack kanal● Design patterns in Kotlin● Keddit - demo app● Kotlin For Android (at DevFest İzmir 2016)● Kotlin – Ready for Production – Hadi Hariri● Android development with Kotlin – Jake Wharton

Questions?.Answers!!