First few months with otlin
Introduction through Android examples
Nebojša Vukšić
Nebojša Vukšić
Android developer @ codecentric
Founder of Kotlin User Group Serbia
Nesh_Wolf
nebojsa92vuksic@gmail.com
Kotlin User Group Serbia
https://www.meetup.com/Serbia-Kotlin-User-Group
https://www.facebook.com/kotlinserbia/
https://twitter.com/kotlin_serbia
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
Variables
val 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
Nullability
var 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
Functions
fun 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”)
}
Functions
fun 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”
Functions
fun 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”
Functions
fun 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 classes
data class User (
val name: String,
val email: String,
val age: Int
)
val user = User("John Smith", "johnsmith@mail.com", 24)
val newUser = user.copy(name = "Sam")
//newUser == User("Sam", "johnsmith@mail.com", 24)
val (name, email, age) = newUser
Data classes
data class User (
val name: String = "John Smith",
val email: String = "johnsmith@mail.com",
val age: Int = 24
)
val user = User ()
// user == User("John Smith", "johnsmith@mail.com", 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 functions
fun 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 functions
package com.app.android.util
fun String.swapSpacesForUnderscore(): String = this.replace(" ", "_")
package com.app.android.main
"Random text!".swapSpacesForUnderscore()
Extension functions
package 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 plugin
class 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 plugin
class 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 plugin
import kotlinx.android.synthetic.main.activity_main.toolbar
import 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 plugin
import kotlinx.android.synthetic.main.activity_main.toolbar
import 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 plugin
import kotlinx.android.synthetic.main.activity_main.toolbar
import 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 plugin
import kotlinx.android.synthetic.main.activity_main.toolbar
import 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("john@mail.com")
● 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("john@mail.com")
Function expressions
val add: (Int, Int) -> Int = { x,y -> x+y }
add(1,2)
val validator: (String) -> Boolean ={ it.contains("@") }
validator("john@mail.com")
mailTextview.validateWith{ validator("john@mail.com") }
● function expressions are blocks of code which we can
instantiate(represent as type)
Higher order functions
Higher order functions
fun <T> List<T>.filter(predicate: (T) -> Boolean): List<T>{
}
Higher order functions
fun <T> List<T>.filter(predicate: (T) -> Boolean): List<T>{
val items = ArrayList<T>()
return items
}
Higher order functions
fun <T> List<T>.filter(predicate: (T) -> Boolean): List<T>{
val items = ArrayList<T>()
for (item in this) {
}
return items
}
Higher order functions
fun <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 functions
fun <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 combo
fun 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 combo
fun 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 combo
From 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!!
Nebojša Vukšić
Android developer @ codecentric
Founder of Kotlin User Group Serbia
Nesh_Wolf
nebojsa92vuksic@gmail.com
Kotlin User Group Serbia
https://www.meetup.com/Serbia-Kotlin-User-Group
https://www.facebook.com/kotlinserbia/
https://twitter.com/kotlin_serbia

First few months with Kotlin - Introduction through android examples

  • 1.
    First few monthswith otlin Introduction through Android examples Nebojša Vukšić
  • 2.
    Nebojša Vukšić Android developer@ codecentric Founder of Kotlin User Group Serbia Nesh_Wolf nebojsa92vuksic@gmail.com
  • 3.
    Kotlin User GroupSerbia https://www.meetup.com/Serbia-Kotlin-User-Group https://www.facebook.com/kotlinserbia/ https://twitter.com/kotlin_serbia
  • 4.
  • 5.
    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
  • 6.
    Kotlin history ● developedby JetBrains ● unveiled to public in 2011.(development started in 2012.) ● 1.0 first stable version (February 2016.) ● current version 1.1 RC
  • 7.
    Why do weneed Kotlin?
  • 8.
    Problems that wehave: 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...)
  • 9.
  • 10.
    Variables val 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
  • 11.
  • 12.
    text.length // compilererror var text: String? = null // This can be null or not-null Nullability var 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
  • 13.
    Nullability val s: String?= "This can be null or not-null" val length = s!!.length Making NPE explicit
  • 14.
    Nullability val s: String?= "This can be null or not-null" val length = s!!.length Making NPE explicit
  • 15.
    Functions fun 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”) }
  • 16.
    Functions fun 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”
  • 17.
    Functions fun 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”
  • 18.
    Functions fun 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”
  • 19.
    Functions fun `should equal4`() { assertEquals(4, 2 + 2) } `should equal 4`()
  • 20.
    Kotlin does classesdifferently...
  • 21.
    Classes and dataclasses @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 + '}'; }
  • 22.
    Classes and dataclasses @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 + '}'; }
  • 23.
    Classes and dataclasses @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 + '}'; }
  • 24.
    Classes and dataclasses data class User ( val name: String, val email: String, val age: Int ) val user = User("John Smith", "johnsmith@mail.com", 24) val newUser = user.copy(name = "Sam") //newUser == User("Sam", "johnsmith@mail.com", 24) val (name, email, age) = newUser
  • 25.
    Data classes data classUser ( val name: String = "John Smith", val email: String = "johnsmith@mail.com", val age: Int = 24 ) val user = User () // user == User("John Smith", "johnsmith@mail.com", 24)
  • 26.
    Classes and dataclasses 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:
  • 27.
    Classes and dataclasses 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:
  • 28.
  • 29.
    Extension functions fun 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(," ", "_"); }
  • 30.
    Extension functions package com.app.android.util funString.swapSpacesForUnderscore(): String = this.replace(" ", "_") package com.app.android.main "Random text!".swapSpacesForUnderscore()
  • 31.
    Extension functions package com.app.android.util funString.swapSpacesForUnderscore(): String = this.replace(" ", "_") package com.app.android.main import com.app.android.util.swapSpacesForUnderscore "Random text!".swapSpacesForUnderscore()
  • 32.
    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]; }
  • 33.
    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]; }
  • 34.
    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]; }
  • 35.
    Android extensions plugin nomore findViewById()
  • 36.
    Android extensions plugin classMainActivity : 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() } } }
  • 37.
    Android extensions plugin classMainActivity : 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() } } }
  • 38.
    Android extensions plugin importkotlinx.android.synthetic.main.activity_main.toolbar import 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() } } }
  • 39.
    Android extensions plugin importkotlinx.android.synthetic.main.activity_main.toolbar import 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() } } }
  • 40.
    Android extensions plugin importkotlinx.android.synthetic.main.activity_main.toolbar import 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() } } }
  • 41.
    Android extensions plugin importkotlinx.android.synthetic.main.activity_main.toolbar import 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()} } }
  • 42.
  • 43.
    Function expressions val add:(Int, Int) -> Int = { x,y -> x+y } add(1,2) val validator: (String) -> Boolean ={ value -> value.contains("@") } validator("john@mail.com") ● function expressions are blocks of code which we can instantiate(represent as type)
  • 44.
    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("john@mail.com")
  • 45.
    Function expressions val add:(Int, Int) -> Int = { x,y -> x+y } add(1,2) val validator: (String) -> Boolean ={ it.contains("@") } validator("john@mail.com") mailTextview.validateWith{ validator("john@mail.com") } ● function expressions are blocks of code which we can instantiate(represent as type)
  • 46.
  • 47.
    Higher order functions fun<T> List<T>.filter(predicate: (T) -> Boolean): List<T>{ }
  • 48.
    Higher order functions fun<T> List<T>.filter(predicate: (T) -> Boolean): List<T>{ val items = ArrayList<T>() return items }
  • 49.
    Higher order functions fun<T> List<T>.filter(predicate: (T) -> Boolean): List<T>{ val items = ArrayList<T>() for (item in this) { } return items }
  • 50.
    Higher order functions fun<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")
  • 51.
    Higher order functions fun<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")
  • 52.
  • 53.
    Quick overview ● Extensionfunctions - 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
  • 54.
    Combination of thisthree makes powerful combo
  • 55.
    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() }
  • 56.
    Extension / Higherorder function expression combo fun 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() }
  • 57.
    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() }
  • 58.
    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() }
  • 59.
    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() }
  • 60.
    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() }
  • 61.
    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() }
  • 62.
    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() }
  • 63.
    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() }
  • 64.
    Extension / Higherorder 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”) } }
  • 65.
    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() }
  • 66.
    Extension / Higherorder function expression combo fun 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() }
  • 67.
    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() }
  • 68.
    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)
  • 69.
    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() }
  • 70.
    Extension / Higherorder function expression combo From 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) } }
  • 71.
    Summary don’t worry, nextslide is the last
  • 72.
    Summary ● immutable andmutable 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
  • 73.
    Resources ● Official Kotlindocumentation ● 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
  • 74.
  • 76.
    Nebojša Vukšić Android developer@ codecentric Founder of Kotlin User Group Serbia Nesh_Wolf nebojsa92vuksic@gmail.com Kotlin User Group Serbia https://www.meetup.com/Serbia-Kotlin-User-Group https://www.facebook.com/kotlinserbia/ https://twitter.com/kotlin_serbia