Mastering
Kotlin Standard
Library
Nelson Glauber
@nglauber
• Provides the essential types, classes and functions for
everyday work with Kotlin.
• In version 1.3 it contains approximately 50 sub packages
organized in 4 categories: 

Common, JVM, JS and Native.
Kotlin Std Lib
• Provides the essential types, classes and functions for
everyday work with Kotlin.
• In version 1.3 it contains approximately 50 sub packages
organized in 4 categories: 

Common, JVM, JS and Native.
Kotlin Std Lib
Scope Functions
• T.let { }
• T.apply { }
• T.also { }
• T.run { }
• run { }
• with(T) { }
Scope Functions
• It’s an extension function.

• Caller is represented by it.

• Returns the block’s last line.
• It’s commonly used for nullability checking.
public inline fun <T, R> T.let(block: (T) -> R): R {
return block(this)
}
fun deleteTempPhoto() {
var tempImageFile: File? = // ...
tempImageFile?.let {
if (it.exists()) it.delete()
}
}
fun deleteTempPhoto() {
var tempImageFile: File? = // ...
if (tempImageFile != null) {
if (tempImageFile.exists()) // thanks smart cast!
tempImageFile.delete()
}
}
class SomeClass {
var tempImageFile: File? = null
fun deleteTempPhoto() {
if (tempImageFile != null && tempImageFile.exists())
tempImageFile.delete()
}
}
class SomeClass {
var tempImageFile: File? = null
fun deleteTempPhoto() {
if (tempImageFile != null && tempImageFile.exists())
tempImageFile.delete()
}
}
Smart	cast	to	'File'	is	impossible,		
	because	'tempImageFile'	is	a	mutable		
	property	that	could	have	been	changed		
	by	this	time	
class SomeClass {
var tempImageFile: File? = null
fun deleteTempPhoto() {
if (tempImageFile != null && tempImageFile.exists())
tempImageFile.delete()
}
}
class SomeClass {
var tempImageFile: File? = null
fun deleteTempPhoto() {
if (tempImageFile != null && tempImageFile?.exists() == true )
tempImageFile?.delete()
}
}
😒😱
class SomeClass {
var tempImageFile: File? = null
fun deleteTempPhoto() {
tempImageFile?.let {
if (it.exists()) it.delete()
}
}
}
class SomeClass {
var tempImageFile: File? = null
fun deleteTempPhoto() {
tempImageFile?.let { file ->
if (file.exists()) file.delete()
}
}
}
😎
• It’s an extension function.

• Caller is represented by this.

• Returns the caller itself (this).
• It’s commonly used for initializing and configuring an
object.
public inline fun <T> T.apply(block: T.() -> Unit): T {
block()
return this
}
val chip = Chip(this)
chip.text = weatherType.name.capitalize()
chip.setChipBackgroundColorResource(R.color.colorAccent)
chip.isClickable = false
chip.isCheckable = false
chip.isCloseIconVisible = true
chip.setOnCloseIconClickListener {
if (viewModel.unselectWeatherType(weatherType)) {
chipGroup.removeView(it)
}
}
chipGroup.addView(chip)
val chip = Chip(this)
chip.text = weatherType.name.capitalize()
chip.setChipBackgroundColorResource(R.color.colorAccent)
chip.isClickable = false
chip.isCheckable = false
chip.isCloseIconVisible = true
chip.setOnCloseIconClickListener {
if (viewModel.unselectWeatherType(weatherType)) {
chipGroup.removeView(it)
}
}
chipGroup.addView(chip)
val chip = Chip(this).apply {
text = weatherType.name.capitalize()
setChipBackgroundColorResource(R.color.colorAccent)
isClickable = false
isCheckable = false
isCloseIconVisible = true
setOnCloseIconClickListener {
if (viewModel.unselectWeatherType(weatherType)) {
chipGroup.removeView(it)
}
}
}
chipGroup.addView(chip)
• It’s an extension function.

• Caller is represented by it.

• Returns the caller itself (this).
• It’s commonly used for chaining object initialization or
simple to separate one action from another.
public inline fun <T> T.also(block: (T) -> Unit): T {
block(this)
return this
}
val chip = Chip(this).apply {
text = weatherType.name.capitalize()
setChipBackgroundColorResource(R.color.colorAccent)
isClickable = false
isCheckable = false
isCloseIconVisible = true
setOnCloseIconClickListener {
if (viewModel.unselectWeatherType(weatherType)) {
chipGroup.removeView(it)
}
}
}
chipGroup.addView(chip)
Chip(this).apply {
text = weatherType.name.capitalize()
setChipBackgroundColorResource(R.color.colorAccent)
isClickable = false
isCheckable = false
isCloseIconVisible = true
setOnCloseIconClickListener {
if (viewModel.unselectWeatherType(weatherType)) {
chipGroup.removeView(it)
}
}
}.also {
chipGroup.addView(it)
}
• It’s an extension function.

• Caller is represented by this.

• Returns the block’s last line.
• It’s commonly used to get the result of an operation
using an object.
public inline fun <T, R> T.run(block: T.() -> R): R {
return block()
}
public inline fun <R> run(block: () -> R): R {
return block()
}
• It’s not an extension function.

• There’s no reference to the caller (once it’s not an
extension function).

• Returns the block’s last line.
• It’s commonly used to get the result of an operation.
private fun getFavoriteCity(): String {
val prefs =
PreferenceManager.getDefaultSharedPreferences(this)
return prefs.getString("favCity", null) ?:
getString(R.string.pref_city_default)
}
private fun getFavoriteCity() =
PreferenceManager.getDefaultSharedPreferences(this).run {
getString("favCity", null)
} ?: run {
getString(R.string.pref_city_default)
}
• It’s not an extension function.

• The caller is passed as parameter and is referenced by
this.

• Returns the block’s last line.
• It’s commonly used when you want to remember Delphi.
public inline fun <T, R> with(receiver: T, block: T.() -> R): R {
return receiver.block()
}
val webView: WebView = loadWebView()
with(webView) {
settings.javaScriptEnabled = true
loadUrl("file:///android_asset/app_page.html")
}
val webView: WebView? = loadWebView()
with(webView) {
settings.javaScriptEnabled = true
loadUrl("file:///android_asset/app_page.html")
}
val webView: WebView? = loadWebView()
with(webView) {
settings.javaScriptEnabled = true
loadUrl("file:///android_asset/app_page.html")
}
	Only	safe	(?.)	or	non-null	asserted	(!!.)		
	calls	are	allowed	on	a	nullable	receiver		
	of	type	WebView?
val webView: WebView? = loadWebView()
with(webView) {
this?.settings?.javaScriptEnabled = true
this?.loadUrl("file:///android_asset/app_page.html")
}
😩
val webView: WebView? = loadWebView()
webView?.let {
it.settings.javaScriptEnabled = true
it.loadUrl("file:///android_asset/app_page.html")
}
%
• T.let { } for nullability check.
• T.apply { } for object initialization.
• T.also { } for initialization chaining or a simple change of
scope.
• T.run { } to get a result from an object sequence of calls.
• run { } to get a result from a sequence of calls.
• with(T) { } for some reason you don’t know how to
explain… :)
Scope Functions - Summary
Annotations
fun sayHello() {
println("Hello!")
}
fun sayHello() {
println("Hello!")
}
fun greetings(message: String = "Hello") {
println(message)
}
@Deprecated(
message = "This function is being replaced.",
replaceWith = ReplaceWith("greetings(message)"),
level = DeprecationLevel.WARNING)
fun sayHello() {
println("Hello!")
}
fun greetings(message: String = "Hello") {
println(message)
}
@Deprecated(
message = "This function is being replaced.",
replaceWith = ReplaceWith("greetings(message)"),
level = DeprecationLevel.WARNING)
fun sayHello() {
println("Hello!")
}
fun greetings(message: String = "Hello") {
println(message)
}
fun main() {
sayHello()
}
fun login(userName: String, password: String) {
// Do login
}
fun login(userName: String, password: String) {
// Do login
}
data class AuthUser(val name: String, val password: String)
fun login(authUser: AuthUser) {
// New login with user
}
fun login(userName: String, password: String) {
// Do login
}
@Experimental(Experimental.Level.WARNING)
annotation class NewApi
data class AuthUser(val name: String, val password: String)
fun login(authUser: AuthUser) {
// New login with user
}
fun login(userName: String, password: String) {
// Do login
}
@Experimental(Experimental.Level.WARNING)
annotation class NewApi
@NewApi
data class AuthUser(val name: String, val password: String)
@NewApi
fun login(authUser: AuthUser) {
// New login with user
}
fun login(userName: String, password: String) {
// Do login
}
@Experimental(Experimental.Level.WARNING)
annotation class NewApi
@NewApi
data class AuthUser(val name: String, val password: String)
@NewApi
fun login(authUser: AuthUser) {
// New login with user
}
@UseExperimental(NewApi::class)
fun main() {
login(AuthUser("ngvl", "123"))
}
• JvmStatic
• JvmOverloads
• JvmName
• JvmField
• Throws
@Jvm* annotations
object UserRepository {
fun insertUser(user: User) {
// insert user
}
}
object UserRepository {
fun insertUser(user: User) {
// insert user
}
}
public class MyJavaClass {
...
public void addUser() {
UserRepository.INSTANCE.insertUser(new User("Nelson"));
}
}
object UserRepository {
@JvmStatic
fun insertUser(user: User) {
// insert user
}
}
public class MyJavaClass {
...
public void addUser() {
UserRepository.INSTANCE.insertUser(new User("Nelson"));
}
}
object UserRepository {
@JvmStatic
fun insertUser(user: User) {
// insert user
}
}
public class MyJavaClass {
...
public void addUser() {
UserRepository.insertUser(new User("Nelson"));
}
}
class MyKotlinClass {
fun printName(name: String = "Anonymous") {
println("Hello $name!")
}
//...
}
class MyKotlinClass {
fun printName(name: String = "Anonymous") {
println("Hello $name!")
}
//...
}
MyKotlinClass instance = new MyKotlinClass();
instance.printName("Nelson"); // Works!
instance.printName(); // Error!
class MyKotlinClass {
@JvmOverloads
fun printName(name: String = "Anonymous") {
println("Hello $name!")
}
//...
}
class MyKotlinClass {
@JvmOverloads
fun printName(name: String = "Anonymous") {
println("Hello $name!")
}
//...
}
MyKotlinClass instance = new MyKotlinClass();
instance.printName("Nelson");
instance.printName();
public class MyCustomView extends LinearLayout {
public MyCustomView(Context context) {
super(context);
}
public MyCustomView(Context context,
@Nullable AttributeSet attrs) {
super(context, attrs);
}
public MyCustomView(Context context,
@Nullable AttributeSet attrs,
int defStyleAttr) {
super(context, attrs, defStyleAttr);
}
...
}
class MyCustomView : LinearLayout {
constructor(context: Context) : super(context) {
}
constructor(context: Context,
attrs: AttributeSet?
) : super(context, attrs) {
}
constructor(context: Context,
attrs: AttributeSet?,
defStyleAttr: Int
) : super(context, attrs, defStyleAttr) {
}
class MyCustomView @JvmOverloads constructor(
context: Context,
attrs: AttributeSet? = null,
defStyleAttr: Int = 0
) : LinearLayout(context, attrs, defStyleAttr) {
...
}
class ToggleView {
private var onOff = false
fun switch() {
onOff = !onOff
}
}
ToggleView toggleView = new ToggleView();
toggleView.switch(); // error!
class ToggleView {
private var onOff = false
@JvmName("switchOnOff")
fun switch() {
onOff = !onOff
}
}
ToggleView toggleView = new ToggleView();
toggleView.switchOnOff();
// StringsExtensions.kt
package ngvl.kotlin.demo.string
fun String.shouldCapitalizeName() =
this !in listOf("da", "das", "de", "di", "do", "dos")
fun String.capitalizeName() =
this.split(" ")
.joinToString(" ") {
if (it.shouldCapitalizeName())
it.capitalize()
else it
}
// Some Java file...
String name = StringExtensionsKt.capitalizeName("nelson glauber");
// StringsExtensions.kt
@file:JvmName("StringExt")
package ngvl.kotlin.demo.string
fun String.shouldCapitalizeName() =
this !in listOf("da", "das", "de", "di", "do", "dos")
fun String.capitalizeName() =
this.split(" ")
.joinToString(" ") {
if (it.shouldCapitalizeName())
it.capitalize()
else it
}
// Some Java file...
String name = StringExtensionsKt.capitalizeName("nelson glauber");
// StringsExtensions.kt
@file:JvmName("StringExt")
package ngvl.kotlin.demo.string
fun String.shouldCapitalizeName() =
this !in listOf("da", "das", "de", "di", "do", "dos")
fun String.capitalizeName() =
this.split(" ")
.joinToString(" ") {
if (it.shouldCapitalizeName())
it.capitalize()
else it
}
// Some Java file...
String name = StringExt.capitalizeName("nelson glauber");
data class Product(
val id: String,
val description: String,
val price: Float = 0f
)
Product product = new Product("001", "Smartphone", 0f);
System.out.println(
product.getId() +" - "+
product.getDescription() +" = "+
product.getPrice()
);
Product product = new Product("001", "Smartphone", 0f);
System.out.println(
product.getId() +" - "+
product.getDescription() +" = "+
product.getPrice()
);
data class Product(
@JvmField val id: String,
@JvmField val description: String,
@JvmField val price: Float = 0f
)
data class Product(
@JvmField val id: String,
@JvmField val description: String,
@JvmField val price: Float = 0f
)
Product product = new Product("001", "Smartphone", 0f);
System.out.println(
product.id +" - "+
product.description +" = "+
product.price
);
class MyKotlinClass {
companion object {
val CONSTANT_1 = 1
const val CONSTANT_2 = 2
@JvmField val CONSTANT_3 = 3
@JvmField val ROOT = User("root")
}
}
MyKotlinClass.Companion.getCONSTANT_1();
MyKotlinClass.CONSTANT_2;
MyKotlinClass.CONSTANT_3;
MyKotlinClass.ROOT;
object UserRepository {
@JvmStatic
fun insertUser(user: User) {
if (user.name.length < 5)
throw NameTooShortException()
// insert user ...
}
}
UserRepository.insertUser(new User("ngvl"));
object UserRepository {
@JvmStatic
@Throws(NameTooShortException::class)
fun insertUser(user: User) {
if (user.name.length < 5) {
throw NameTooShortException()
}
// insert user ...
}
}
UserRepository.insertUser(new User("ngvl"));
try {
UserRepository.insertUser(new User("ngvl"));
} catch (NameTooShortException e) {
// handle name too short
}
object UserRepository {
@JvmStatic
@Throws(NameTooShortException::class)
fun insertUser(user: User) {
if (user.name.length < 5) {
throw NameTooShortException()
}
// insert user ...
}
}
data class Recipe(
var name: String? = null,
var ingredients: List<Ingredient> = mutableListOf()
)
data class Ingredient(
var description: String? = null,
var quantity: Int = 0,
var unity: String? = null
)
data class Recipe(
var name: String? = null,
var ingredients: List<Ingredient> = mutableListOf()
)
data class Ingredient(
var description: String? = null,
var quantity: Int = 0,
var unity: String? = null
)
fun recipe(build: Recipe.() -> Unit) = Recipe().apply(build)
fun Recipe.ingredient(build: Ingredient.() -> Unit) {
ingredients += Ingredient().apply(build)
}
public inline fun <T> T.apply(block: T.() -> Unit): T {
block()
return this
}
fun recipe(build: Recipe.() -> Unit) = Recipe().apply(build)
fun Recipe.ingredient(build: Ingredient.() -> Unit) {
ingredients += Ingredient().apply(build)
}
val recipe = recipe {
name = "Chocolate Cake"
ingredient {
name = "Flour"
quantity = 3
unity = "cups"
}
}
println(recipe)
Recipe(name=Flour,	ingredients=[	
				Ingredient(description=null,	quantity=3,	unity=cups)]	
) 🤔
data class Recipe(
var name: String? = null,
var ingredients: List<Ingredient> = mutableListOf()
)
data class Ingredient(
var description: String? = null,
var quantity: Int = 0,
var unity: String? = null
)
fun recipe(build: Recipe.() -> Unit) = Recipe().apply(build)
fun Recipe.ingredient(build: Ingredient.() -> Unit) {
ingredients += Ingredient().apply(build)
}
@DslMarker annotation class RecipeLang
@RecipeLang
data class Recipe(
var name: String? = null,
var ingredients: List<Ingredient> = mutableListOf()
)
@RecipeLang
data class Ingredient(
var description: String? = null,
var quantity: Int = 0,
var unity: String? = null
)
fun recipe(build: Recipe.() -> Unit) = Recipe().apply(build)
fun Recipe.ingredient(build: Ingredient.() -> Unit) {
ingredients += Ingredient().apply(build)
}
val recipe = recipe {
name = "Chocolate Cake"
ingredient {
name = "Flour"
quantity = 3
unity = "cups"
}
}
println(recipe)
fun recipe(build: Recipe.() -> Unit) = Recipe().apply(build)
fun Recipe.ingredient(build: Ingredient.() -> Unit) {
ingredients += Ingredient().apply(build)
}
Compile	error:	'var	name:	String?'		
can't	be	called	in	this	context	by		
implicit	receiver.		
Use	the	explicit	one	if	necessary
val recipe = recipe {
name = "Chocolate Cake"
ingredient {
this@recipe.name = "Choc.Cake"
quantity = 3
unity = "cups"
}
}
println(recipe)
fun recipe(build: Recipe.() -> Unit) = Recipe().apply(build)
fun Recipe.ingredient(build: Ingredient.() -> Unit) {
ingredients += Ingredient().apply(build)
}
val recipe = recipe {
name = "Chocolate Cake"
ingredient {
description = "Flour"
quantity = 3
unity = "cups"
}
}
println(recipe)
fun recipe(build: Recipe.() -> Unit) = Recipe().apply(build)
fun Recipe.ingredient(build: Ingredient.() -> Unit) {
ingredients += Ingredient().apply(build)
}
Recipe(name=Chocolate	Cake,	ingredients=[	
				Ingredient(description=Flour,	quantity=3,	unity=cups)]	
) 😋
• @Deprecated and @Experimental help to document the
evolution of your code
• @Jvm* annotations help the integration with Java code.
• @DslMarker create scopes for your DSLs.
Annotations - Summary
Delegates
abstract class Individual(
val name: String,
val surname: String,
val age: Int
)
class Student(
name: String,
surname: String,
age: Int,
val university: String
) : Individual(name, surname, age)
class Worker(
name: String,
surname: String,
age: Int,
val company: String
) : Individual(name, surname, age)
abstract class Individual(
val name: String,
val surname: String,
val age: Int
)
class Student(
name: String,
surname: String,
age: Int,
val university: String
) : Individual(name, surname, age)
class Worker(
name: String,
surname: String,
age: Int,
val company: String
) : Individual(name, surname, age)
interface Individual {
val name: String
val surname: String
val age: Int
}
data class IndividualData(
override val name: String,
override val surname: String,
override val age: Int
) : Individual
data class Student(
val data: IndividualData,
val university: String
) : Individual by data
data class Worker(
val data: IndividualData,
val company: String
) : Individual by data
interface Individual {
val name: String
val surname: String
val age: Int
}
data class IndividualData(
override val name: String,
override val surname: String,
override val age: Int
) : Individual
data class Student(
val data: IndividualData,
val university: String
) : Individual by data
data class Worker(
val data: IndividualData,
val company: String
) : Individual by data
val student = Student(
IndividualData("Nelson", "Leal", 36), "Universo"
)
student.run {
println("$name $surname - $age - $university")
}
	Nelson	Leal	-	36	-	Universo
class Repository {
private val peopleDAO: PeopleDAO by lazy {
Database.getPeopleDAO()
}
fun insert(person: Person) {
peopleDAO.add(person)
}
fun all(): List<Person> = peopleDAO.list()
}
class Repository {
private val peopleDAO: PeopleDAO by lazy {
Database.getPeopleDAO()
}
fun insert(person: Person) {
peopleDAO.add(person)
}
fun all(): List<Person> = peopleDAO.list()
}
class Repository {
private val peopleDAO: PeopleDAO by lazy {
Database.getPeopleDAO()
}
fun insert(person: Person) {
peopleDAO.add(person)
}
fun all(): List<Person> = peopleDAO.list()
}
val repo = Repository()
repo.insert(Person("1", "Nelson"))
repo.insert(Person("2", "Glauber"))
repo.all().forEach(::printPerson)
Delegates.vetoable(0) { // it's an inline function
kprop, oldValue, newValue ->
newValue in 0..122 // Jeanne Calmen
}
val ageDelegate = {
Delegates.vetoable(0) {
kprop, oldValue, newValue ->
newValue in 0..122
}
}
if you don't use it as a function, it will return
the last value for the new instance
val ageDelegate = {
Delegates.vetoable(0) {
kprop, oldValue, newValue ->
newValue in 0..122
}
}
class Person(val id: String, val name: String) {
var age: Int by ageDelegate()
}
val ageDelegate = {
Delegates.vetoable(0) {
kprop, oldValue, newValue ->
newValue in 0..122
}
}
class Person(val id: String, val name: String) {
var age: Int by ageDelegate()
}
val p = Person("3", "Nelson")
p.age = -1
printPerson(p)
p.age = 150
printPerson(p)
p.age = 35
printPerson(p)
	3	-	Nelson	-	0	
	3	-	Nelson	-	0	
		
	3	-	Nelson	-	35
interface OnFilterCallback {
fun onFilterChanged(f: Filter)
}
class Filter {
var minBathNum: Int = 0
var minPrice: Float = 0f
private val callbacks = mutableSetOf<OnFilterCallback>()
fun addOnFilterChangedCallback(callback: OnFilterCallback) {
callbacks += callback
}
}
class Filter {
var minBathNum: Int = 0
var minPrice: Float = 0f
private val callbacks = mutableSetOf<OnFilterCallback>()
fun addOnFilterChangedCallback(callback: OnFilterCallback) {
callbacks += callback
}
fun notifyObservers() {
callbacks.forEach{ it.onFilterChanged(this) }
}
}
class FilterDelegate<T>(initial: T) {
var internal: T = initial
operator fun getValue(thisRef: Filter, prop: KProperty<*>): T {
return internal
}
operator fun setValue(thisRef: Filter, prop: KProperty<*>, value: T) {
internal = value
thisRef.notifyObservers()
}
}
interface OnFilterCallback {
fun onFilterChanged(f: Filter)
}
class Filter {
var minBathNum: Int by FilterDelegate(0)
var minPrice: Float by FilterDelegate(0f)
private val callbacks = mutableSetOf<OnFilterCallback>()
fun addOnFilterChangedCallback(callback: OnFilterCallback) {
callbacks += callback
}
fun notifyObservers() {
callbacks.forEach{ it.onFilterChanged(this) }
}
}
val filter = Filter()
filter.addOnFilterChangedCallback(object: OnFilterCallback {
override fun onFilterChanged(f: Filter) {
println("Observer #1")
println("MinPrice: ${f.minPrice} - Baths:${f.minBathNum}")
}
})
filter.addOnFilterChangedCallback(object : OnFilterCallback {
override fun onFilterChanged(f: Filter) {
println("Observer #2")
println("MinPrice: ${f.minPrice} - Baths:${f.minBathNum}")
}
})
filter.minBathNum = 2
filter.minPrice = 100_000f
filter.minBathNum = 1
filter.minPrice = 10f
Observer	#1	|	MinPrice:	0.0	-	Baths:2	
Observer	#2	|	MinPrice:	0.0	-	Baths:2	
Observer	#1	|	MinPrice:	100000.0	-	Baths:2	
Observer	#2	|	MinPrice:	100000.0	-	Baths:2	
Observer	#1	|	MinPrice:	100000.0	-	Baths:1	
Observer	#2	|	MinPrice:	100000.0	-	Baths:1	
Observer	#1	|	MinPrice:	10.0	-	Baths:1	
Observer	#2	|	MinPrice:	10.0	-	Baths:1
fun <T> Filter.filterDelegate(initialValue: T) =
Delegates.observable(initialValue) { prop, oldValue, newValue ->
println("old: $oldValue -> new: $newValue")
this.notifyObservers()
}
fun <T> Filter.filterDelegate(initialValue: T) =
Delegates.observable(initialValue) { prop, oldValue, newValue ->
println("old: $oldValue -> new: $newValue")
this.notifyObservers()
}
class Filter {
var minBathNum: Int by filterDelegate(0)
var minPrice: Float by filterDelegate(0f)
...
}
val filter = Filter()
filter.addOnFilterChangedCallback(object: OnFilterCallback {
override fun onFilterChanged(f: Filter) {
println("Observer #1”)
}
})
filter.addOnFilterChangedCallback(object : OnFilterCallback {
override fun onFilterChanged(f: Filter) {
println("Observer #2")
}
})
filter.minBathNum = 2
filter.minPrice = 100_000f
filter.minBathNum = 1
filter.minPrice = 10f
old:	0	->	new:	2	
Observer	#1	
Observer	#2	
old:	0.0	->	new:	100000.0	
Observer	#1	
Observer	#2	
old:	2	->	new:	1	
Observer	#1	
Observer	#2	
old:	100000.0	->	new:	10.0	
Observer	#1	
Observer	#2
• Interface delegation for composition.
• lazy for… lazy initialization.
• vetoable to define a validation to be reused.
• observable if you need both old and new value.
• Custom delegates for everything else.
Delegates - Summary
• Std Lib has a lot of functions to help you to write a more
simple, readable and concise code.
• Check it out the lib internals. You’ll learn a lot!
• Check it out the Kotlin Koans (https://play.kotlinlang.org/
koans). It’s a good place to start (or recap)!
• Write Kotlin code, enjoy the std lib and have fun! 😉
Wrap up!
• Kotlin Standard Functions

https://medium.com/androiddevelopers/kotlin-standard-functions-
cheat-sheet-27f032dd4326
• Mastering Kotlin standard functions: run, with, let, also and apply

https://medium.com/@elye.project/mastering-kotlin-standard-
functions-run-with-let-also-and-apply-9cd334b0ef84
• Java Friendly Kotlin

https://codelabs.developers.google.com/codelabs/java-friendly-kotlin
• Simpler Kotlin class hierarchies using class delegation

https://proandroiddev.com/simpler-kotlin-class-hierarchies-using-
class-delegation-35464106fed5
References
Gracias!
Nelson Glauber
@nglauber

Mastering Kotlin Standard Library

  • 1.
  • 2.
    • Provides theessential types, classes and functions for everyday work with Kotlin. • In version 1.3 it contains approximately 50 sub packages organized in 4 categories: 
 Common, JVM, JS and Native. Kotlin Std Lib
  • 3.
    • Provides theessential types, classes and functions for everyday work with Kotlin. • In version 1.3 it contains approximately 50 sub packages organized in 4 categories: 
 Common, JVM, JS and Native. Kotlin Std Lib
  • 4.
  • 5.
    • T.let {} • T.apply { } • T.also { } • T.run { } • run { } • with(T) { } Scope Functions
  • 6.
    • It’s anextension function. • Caller is represented by it. • Returns the block’s last line. • It’s commonly used for nullability checking. public inline fun <T, R> T.let(block: (T) -> R): R { return block(this) }
  • 7.
    fun deleteTempPhoto() { vartempImageFile: File? = // ... tempImageFile?.let { if (it.exists()) it.delete() } }
  • 8.
    fun deleteTempPhoto() { vartempImageFile: File? = // ... if (tempImageFile != null) { if (tempImageFile.exists()) // thanks smart cast! tempImageFile.delete() } }
  • 9.
    class SomeClass { vartempImageFile: File? = null fun deleteTempPhoto() { if (tempImageFile != null && tempImageFile.exists()) tempImageFile.delete() } }
  • 10.
    class SomeClass { vartempImageFile: File? = null fun deleteTempPhoto() { if (tempImageFile != null && tempImageFile.exists()) tempImageFile.delete() } }
  • 11.
    Smart cast to 'File' is impossible, because 'tempImageFile' is a mutable property that could have been changed by this time class SomeClass { vartempImageFile: File? = null fun deleteTempPhoto() { if (tempImageFile != null && tempImageFile.exists()) tempImageFile.delete() } }
  • 12.
    class SomeClass { vartempImageFile: File? = null fun deleteTempPhoto() { if (tempImageFile != null && tempImageFile?.exists() == true ) tempImageFile?.delete() } } 😒😱
  • 13.
    class SomeClass { vartempImageFile: File? = null fun deleteTempPhoto() { tempImageFile?.let { if (it.exists()) it.delete() } } }
  • 14.
    class SomeClass { vartempImageFile: File? = null fun deleteTempPhoto() { tempImageFile?.let { file -> if (file.exists()) file.delete() } } } 😎
  • 15.
    • It’s anextension function. • Caller is represented by this. • Returns the caller itself (this). • It’s commonly used for initializing and configuring an object. public inline fun <T> T.apply(block: T.() -> Unit): T { block() return this }
  • 16.
    val chip =Chip(this) chip.text = weatherType.name.capitalize() chip.setChipBackgroundColorResource(R.color.colorAccent) chip.isClickable = false chip.isCheckable = false chip.isCloseIconVisible = true chip.setOnCloseIconClickListener { if (viewModel.unselectWeatherType(weatherType)) { chipGroup.removeView(it) } } chipGroup.addView(chip)
  • 17.
    val chip =Chip(this) chip.text = weatherType.name.capitalize() chip.setChipBackgroundColorResource(R.color.colorAccent) chip.isClickable = false chip.isCheckable = false chip.isCloseIconVisible = true chip.setOnCloseIconClickListener { if (viewModel.unselectWeatherType(weatherType)) { chipGroup.removeView(it) } } chipGroup.addView(chip)
  • 18.
    val chip =Chip(this).apply { text = weatherType.name.capitalize() setChipBackgroundColorResource(R.color.colorAccent) isClickable = false isCheckable = false isCloseIconVisible = true setOnCloseIconClickListener { if (viewModel.unselectWeatherType(weatherType)) { chipGroup.removeView(it) } } } chipGroup.addView(chip)
  • 19.
    • It’s anextension function. • Caller is represented by it. • Returns the caller itself (this). • It’s commonly used for chaining object initialization or simple to separate one action from another. public inline fun <T> T.also(block: (T) -> Unit): T { block(this) return this }
  • 20.
    val chip =Chip(this).apply { text = weatherType.name.capitalize() setChipBackgroundColorResource(R.color.colorAccent) isClickable = false isCheckable = false isCloseIconVisible = true setOnCloseIconClickListener { if (viewModel.unselectWeatherType(weatherType)) { chipGroup.removeView(it) } } } chipGroup.addView(chip)
  • 21.
    Chip(this).apply { text =weatherType.name.capitalize() setChipBackgroundColorResource(R.color.colorAccent) isClickable = false isCheckable = false isCloseIconVisible = true setOnCloseIconClickListener { if (viewModel.unselectWeatherType(weatherType)) { chipGroup.removeView(it) } } }.also { chipGroup.addView(it) }
  • 22.
    • It’s anextension function. • Caller is represented by this. • Returns the block’s last line. • It’s commonly used to get the result of an operation using an object. public inline fun <T, R> T.run(block: T.() -> R): R { return block() }
  • 23.
    public inline fun<R> run(block: () -> R): R { return block() } • It’s not an extension function. • There’s no reference to the caller (once it’s not an extension function). • Returns the block’s last line. • It’s commonly used to get the result of an operation.
  • 24.
    private fun getFavoriteCity():String { val prefs = PreferenceManager.getDefaultSharedPreferences(this) return prefs.getString("favCity", null) ?: getString(R.string.pref_city_default) }
  • 25.
    private fun getFavoriteCity()= PreferenceManager.getDefaultSharedPreferences(this).run { getString("favCity", null) } ?: run { getString(R.string.pref_city_default) }
  • 26.
    • It’s notan extension function. • The caller is passed as parameter and is referenced by this. • Returns the block’s last line. • It’s commonly used when you want to remember Delphi. public inline fun <T, R> with(receiver: T, block: T.() -> R): R { return receiver.block() }
  • 27.
    val webView: WebView= loadWebView() with(webView) { settings.javaScriptEnabled = true loadUrl("file:///android_asset/app_page.html") }
  • 28.
    val webView: WebView?= loadWebView() with(webView) { settings.javaScriptEnabled = true loadUrl("file:///android_asset/app_page.html") }
  • 29.
    val webView: WebView?= loadWebView() with(webView) { settings.javaScriptEnabled = true loadUrl("file:///android_asset/app_page.html") } Only safe (?.) or non-null asserted (!!.) calls are allowed on a nullable receiver of type WebView?
  • 30.
    val webView: WebView?= loadWebView() with(webView) { this?.settings?.javaScriptEnabled = true this?.loadUrl("file:///android_asset/app_page.html") } 😩
  • 31.
    val webView: WebView?= loadWebView() webView?.let { it.settings.javaScriptEnabled = true it.loadUrl("file:///android_asset/app_page.html") } %
  • 32.
    • T.let {} for nullability check. • T.apply { } for object initialization. • T.also { } for initialization chaining or a simple change of scope. • T.run { } to get a result from an object sequence of calls. • run { } to get a result from a sequence of calls. • with(T) { } for some reason you don’t know how to explain… :) Scope Functions - Summary
  • 33.
  • 34.
  • 35.
    fun sayHello() { println("Hello!") } fungreetings(message: String = "Hello") { println(message) }
  • 36.
    @Deprecated( message = "Thisfunction is being replaced.", replaceWith = ReplaceWith("greetings(message)"), level = DeprecationLevel.WARNING) fun sayHello() { println("Hello!") } fun greetings(message: String = "Hello") { println(message) }
  • 37.
    @Deprecated( message = "Thisfunction is being replaced.", replaceWith = ReplaceWith("greetings(message)"), level = DeprecationLevel.WARNING) fun sayHello() { println("Hello!") } fun greetings(message: String = "Hello") { println(message) } fun main() { sayHello() }
  • 38.
    fun login(userName: String,password: String) { // Do login }
  • 39.
    fun login(userName: String,password: String) { // Do login } data class AuthUser(val name: String, val password: String) fun login(authUser: AuthUser) { // New login with user }
  • 40.
    fun login(userName: String,password: String) { // Do login } @Experimental(Experimental.Level.WARNING) annotation class NewApi data class AuthUser(val name: String, val password: String) fun login(authUser: AuthUser) { // New login with user }
  • 41.
    fun login(userName: String,password: String) { // Do login } @Experimental(Experimental.Level.WARNING) annotation class NewApi @NewApi data class AuthUser(val name: String, val password: String) @NewApi fun login(authUser: AuthUser) { // New login with user }
  • 42.
    fun login(userName: String,password: String) { // Do login } @Experimental(Experimental.Level.WARNING) annotation class NewApi @NewApi data class AuthUser(val name: String, val password: String) @NewApi fun login(authUser: AuthUser) { // New login with user } @UseExperimental(NewApi::class) fun main() { login(AuthUser("ngvl", "123")) }
  • 43.
    • JvmStatic • JvmOverloads •JvmName • JvmField • Throws @Jvm* annotations
  • 44.
    object UserRepository { funinsertUser(user: User) { // insert user } }
  • 45.
    object UserRepository { funinsertUser(user: User) { // insert user } } public class MyJavaClass { ... public void addUser() { UserRepository.INSTANCE.insertUser(new User("Nelson")); } }
  • 46.
    object UserRepository { @JvmStatic funinsertUser(user: User) { // insert user } } public class MyJavaClass { ... public void addUser() { UserRepository.INSTANCE.insertUser(new User("Nelson")); } }
  • 47.
    object UserRepository { @JvmStatic funinsertUser(user: User) { // insert user } } public class MyJavaClass { ... public void addUser() { UserRepository.insertUser(new User("Nelson")); } }
  • 48.
    class MyKotlinClass { funprintName(name: String = "Anonymous") { println("Hello $name!") } //... }
  • 49.
    class MyKotlinClass { funprintName(name: String = "Anonymous") { println("Hello $name!") } //... } MyKotlinClass instance = new MyKotlinClass(); instance.printName("Nelson"); // Works! instance.printName(); // Error!
  • 50.
    class MyKotlinClass { @JvmOverloads funprintName(name: String = "Anonymous") { println("Hello $name!") } //... }
  • 51.
    class MyKotlinClass { @JvmOverloads funprintName(name: String = "Anonymous") { println("Hello $name!") } //... } MyKotlinClass instance = new MyKotlinClass(); instance.printName("Nelson"); instance.printName();
  • 52.
    public class MyCustomViewextends LinearLayout { public MyCustomView(Context context) { super(context); } public MyCustomView(Context context, @Nullable AttributeSet attrs) { super(context, attrs); } public MyCustomView(Context context, @Nullable AttributeSet attrs, int defStyleAttr) { super(context, attrs, defStyleAttr); } ... }
  • 53.
    class MyCustomView :LinearLayout { constructor(context: Context) : super(context) { } constructor(context: Context, attrs: AttributeSet? ) : super(context, attrs) { } constructor(context: Context, attrs: AttributeSet?, defStyleAttr: Int ) : super(context, attrs, defStyleAttr) { }
  • 54.
    class MyCustomView @JvmOverloadsconstructor( context: Context, attrs: AttributeSet? = null, defStyleAttr: Int = 0 ) : LinearLayout(context, attrs, defStyleAttr) { ... }
  • 55.
    class ToggleView { privatevar onOff = false fun switch() { onOff = !onOff } } ToggleView toggleView = new ToggleView(); toggleView.switch(); // error!
  • 56.
    class ToggleView { privatevar onOff = false @JvmName("switchOnOff") fun switch() { onOff = !onOff } } ToggleView toggleView = new ToggleView(); toggleView.switchOnOff();
  • 57.
    // StringsExtensions.kt package ngvl.kotlin.demo.string funString.shouldCapitalizeName() = this !in listOf("da", "das", "de", "di", "do", "dos") fun String.capitalizeName() = this.split(" ") .joinToString(" ") { if (it.shouldCapitalizeName()) it.capitalize() else it } // Some Java file... String name = StringExtensionsKt.capitalizeName("nelson glauber");
  • 58.
    // StringsExtensions.kt @file:JvmName("StringExt") package ngvl.kotlin.demo.string funString.shouldCapitalizeName() = this !in listOf("da", "das", "de", "di", "do", "dos") fun String.capitalizeName() = this.split(" ") .joinToString(" ") { if (it.shouldCapitalizeName()) it.capitalize() else it } // Some Java file... String name = StringExtensionsKt.capitalizeName("nelson glauber");
  • 59.
    // StringsExtensions.kt @file:JvmName("StringExt") package ngvl.kotlin.demo.string funString.shouldCapitalizeName() = this !in listOf("da", "das", "de", "di", "do", "dos") fun String.capitalizeName() = this.split(" ") .joinToString(" ") { if (it.shouldCapitalizeName()) it.capitalize() else it } // Some Java file... String name = StringExt.capitalizeName("nelson glauber");
  • 60.
    data class Product( valid: String, val description: String, val price: Float = 0f ) Product product = new Product("001", "Smartphone", 0f); System.out.println( product.getId() +" - "+ product.getDescription() +" = "+ product.getPrice() );
  • 61.
    Product product =new Product("001", "Smartphone", 0f); System.out.println( product.getId() +" - "+ product.getDescription() +" = "+ product.getPrice() ); data class Product( @JvmField val id: String, @JvmField val description: String, @JvmField val price: Float = 0f )
  • 62.
    data class Product( @JvmFieldval id: String, @JvmField val description: String, @JvmField val price: Float = 0f ) Product product = new Product("001", "Smartphone", 0f); System.out.println( product.id +" - "+ product.description +" = "+ product.price );
  • 63.
    class MyKotlinClass { companionobject { val CONSTANT_1 = 1 const val CONSTANT_2 = 2 @JvmField val CONSTANT_3 = 3 @JvmField val ROOT = User("root") } } MyKotlinClass.Companion.getCONSTANT_1(); MyKotlinClass.CONSTANT_2; MyKotlinClass.CONSTANT_3; MyKotlinClass.ROOT;
  • 64.
    object UserRepository { @JvmStatic funinsertUser(user: User) { if (user.name.length < 5) throw NameTooShortException() // insert user ... } } UserRepository.insertUser(new User("ngvl"));
  • 65.
    object UserRepository { @JvmStatic @Throws(NameTooShortException::class) funinsertUser(user: User) { if (user.name.length < 5) { throw NameTooShortException() } // insert user ... } } UserRepository.insertUser(new User("ngvl"));
  • 66.
    try { UserRepository.insertUser(new User("ngvl")); }catch (NameTooShortException e) { // handle name too short } object UserRepository { @JvmStatic @Throws(NameTooShortException::class) fun insertUser(user: User) { if (user.name.length < 5) { throw NameTooShortException() } // insert user ... } }
  • 67.
    data class Recipe( varname: String? = null, var ingredients: List<Ingredient> = mutableListOf() ) data class Ingredient( var description: String? = null, var quantity: Int = 0, var unity: String? = null )
  • 68.
    data class Recipe( varname: String? = null, var ingredients: List<Ingredient> = mutableListOf() ) data class Ingredient( var description: String? = null, var quantity: Int = 0, var unity: String? = null ) fun recipe(build: Recipe.() -> Unit) = Recipe().apply(build) fun Recipe.ingredient(build: Ingredient.() -> Unit) { ingredients += Ingredient().apply(build) } public inline fun <T> T.apply(block: T.() -> Unit): T { block() return this }
  • 69.
    fun recipe(build: Recipe.()-> Unit) = Recipe().apply(build) fun Recipe.ingredient(build: Ingredient.() -> Unit) { ingredients += Ingredient().apply(build) } val recipe = recipe { name = "Chocolate Cake" ingredient { name = "Flour" quantity = 3 unity = "cups" } } println(recipe) Recipe(name=Flour, ingredients=[ Ingredient(description=null, quantity=3, unity=cups)] ) 🤔
  • 70.
    data class Recipe( varname: String? = null, var ingredients: List<Ingredient> = mutableListOf() ) data class Ingredient( var description: String? = null, var quantity: Int = 0, var unity: String? = null ) fun recipe(build: Recipe.() -> Unit) = Recipe().apply(build) fun Recipe.ingredient(build: Ingredient.() -> Unit) { ingredients += Ingredient().apply(build) }
  • 71.
    @DslMarker annotation classRecipeLang @RecipeLang data class Recipe( var name: String? = null, var ingredients: List<Ingredient> = mutableListOf() ) @RecipeLang data class Ingredient( var description: String? = null, var quantity: Int = 0, var unity: String? = null ) fun recipe(build: Recipe.() -> Unit) = Recipe().apply(build) fun Recipe.ingredient(build: Ingredient.() -> Unit) { ingredients += Ingredient().apply(build) }
  • 72.
    val recipe =recipe { name = "Chocolate Cake" ingredient { name = "Flour" quantity = 3 unity = "cups" } } println(recipe) fun recipe(build: Recipe.() -> Unit) = Recipe().apply(build) fun Recipe.ingredient(build: Ingredient.() -> Unit) { ingredients += Ingredient().apply(build) } Compile error: 'var name: String?' can't be called in this context by implicit receiver. Use the explicit one if necessary
  • 73.
    val recipe =recipe { name = "Chocolate Cake" ingredient { this@recipe.name = "Choc.Cake" quantity = 3 unity = "cups" } } println(recipe) fun recipe(build: Recipe.() -> Unit) = Recipe().apply(build) fun Recipe.ingredient(build: Ingredient.() -> Unit) { ingredients += Ingredient().apply(build) }
  • 74.
    val recipe =recipe { name = "Chocolate Cake" ingredient { description = "Flour" quantity = 3 unity = "cups" } } println(recipe) fun recipe(build: Recipe.() -> Unit) = Recipe().apply(build) fun Recipe.ingredient(build: Ingredient.() -> Unit) { ingredients += Ingredient().apply(build) } Recipe(name=Chocolate Cake, ingredients=[ Ingredient(description=Flour, quantity=3, unity=cups)] ) 😋
  • 75.
    • @Deprecated and@Experimental help to document the evolution of your code • @Jvm* annotations help the integration with Java code. • @DslMarker create scopes for your DSLs. Annotations - Summary
  • 76.
  • 77.
    abstract class Individual( valname: String, val surname: String, val age: Int ) class Student( name: String, surname: String, age: Int, val university: String ) : Individual(name, surname, age) class Worker( name: String, surname: String, age: Int, val company: String ) : Individual(name, surname, age)
  • 78.
    abstract class Individual( valname: String, val surname: String, val age: Int ) class Student( name: String, surname: String, age: Int, val university: String ) : Individual(name, surname, age) class Worker( name: String, surname: String, age: Int, val company: String ) : Individual(name, surname, age)
  • 79.
    interface Individual { valname: String val surname: String val age: Int } data class IndividualData( override val name: String, override val surname: String, override val age: Int ) : Individual data class Student( val data: IndividualData, val university: String ) : Individual by data data class Worker( val data: IndividualData, val company: String ) : Individual by data
  • 80.
    interface Individual { valname: String val surname: String val age: Int } data class IndividualData( override val name: String, override val surname: String, override val age: Int ) : Individual data class Student( val data: IndividualData, val university: String ) : Individual by data data class Worker( val data: IndividualData, val company: String ) : Individual by data val student = Student( IndividualData("Nelson", "Leal", 36), "Universo" ) student.run { println("$name $surname - $age - $university") } Nelson Leal - 36 - Universo
  • 81.
    class Repository { privateval peopleDAO: PeopleDAO by lazy { Database.getPeopleDAO() } fun insert(person: Person) { peopleDAO.add(person) } fun all(): List<Person> = peopleDAO.list() }
  • 82.
    class Repository { privateval peopleDAO: PeopleDAO by lazy { Database.getPeopleDAO() } fun insert(person: Person) { peopleDAO.add(person) } fun all(): List<Person> = peopleDAO.list() }
  • 83.
    class Repository { privateval peopleDAO: PeopleDAO by lazy { Database.getPeopleDAO() } fun insert(person: Person) { peopleDAO.add(person) } fun all(): List<Person> = peopleDAO.list() } val repo = Repository() repo.insert(Person("1", "Nelson")) repo.insert(Person("2", "Glauber")) repo.all().forEach(::printPerson)
  • 84.
    Delegates.vetoable(0) { //it's an inline function kprop, oldValue, newValue -> newValue in 0..122 // Jeanne Calmen }
  • 85.
    val ageDelegate ={ Delegates.vetoable(0) { kprop, oldValue, newValue -> newValue in 0..122 } } if you don't use it as a function, it will return the last value for the new instance
  • 86.
    val ageDelegate ={ Delegates.vetoable(0) { kprop, oldValue, newValue -> newValue in 0..122 } } class Person(val id: String, val name: String) { var age: Int by ageDelegate() }
  • 87.
    val ageDelegate ={ Delegates.vetoable(0) { kprop, oldValue, newValue -> newValue in 0..122 } } class Person(val id: String, val name: String) { var age: Int by ageDelegate() } val p = Person("3", "Nelson") p.age = -1 printPerson(p) p.age = 150 printPerson(p) p.age = 35 printPerson(p) 3 - Nelson - 0 3 - Nelson - 0 3 - Nelson - 35
  • 88.
    interface OnFilterCallback { funonFilterChanged(f: Filter) } class Filter { var minBathNum: Int = 0 var minPrice: Float = 0f private val callbacks = mutableSetOf<OnFilterCallback>() fun addOnFilterChangedCallback(callback: OnFilterCallback) { callbacks += callback } }
  • 89.
    class Filter { varminBathNum: Int = 0 var minPrice: Float = 0f private val callbacks = mutableSetOf<OnFilterCallback>() fun addOnFilterChangedCallback(callback: OnFilterCallback) { callbacks += callback } fun notifyObservers() { callbacks.forEach{ it.onFilterChanged(this) } } }
  • 90.
    class FilterDelegate<T>(initial: T){ var internal: T = initial operator fun getValue(thisRef: Filter, prop: KProperty<*>): T { return internal } operator fun setValue(thisRef: Filter, prop: KProperty<*>, value: T) { internal = value thisRef.notifyObservers() } }
  • 91.
    interface OnFilterCallback { funonFilterChanged(f: Filter) } class Filter { var minBathNum: Int by FilterDelegate(0) var minPrice: Float by FilterDelegate(0f) private val callbacks = mutableSetOf<OnFilterCallback>() fun addOnFilterChangedCallback(callback: OnFilterCallback) { callbacks += callback } fun notifyObservers() { callbacks.forEach{ it.onFilterChanged(this) } } }
  • 92.
    val filter =Filter() filter.addOnFilterChangedCallback(object: OnFilterCallback { override fun onFilterChanged(f: Filter) { println("Observer #1") println("MinPrice: ${f.minPrice} - Baths:${f.minBathNum}") } }) filter.addOnFilterChangedCallback(object : OnFilterCallback { override fun onFilterChanged(f: Filter) { println("Observer #2") println("MinPrice: ${f.minPrice} - Baths:${f.minBathNum}") } }) filter.minBathNum = 2 filter.minPrice = 100_000f filter.minBathNum = 1 filter.minPrice = 10f Observer #1 | MinPrice: 0.0 - Baths:2 Observer #2 | MinPrice: 0.0 - Baths:2 Observer #1 | MinPrice: 100000.0 - Baths:2 Observer #2 | MinPrice: 100000.0 - Baths:2 Observer #1 | MinPrice: 100000.0 - Baths:1 Observer #2 | MinPrice: 100000.0 - Baths:1 Observer #1 | MinPrice: 10.0 - Baths:1 Observer #2 | MinPrice: 10.0 - Baths:1
  • 93.
    fun <T> Filter.filterDelegate(initialValue:T) = Delegates.observable(initialValue) { prop, oldValue, newValue -> println("old: $oldValue -> new: $newValue") this.notifyObservers() }
  • 94.
    fun <T> Filter.filterDelegate(initialValue:T) = Delegates.observable(initialValue) { prop, oldValue, newValue -> println("old: $oldValue -> new: $newValue") this.notifyObservers() } class Filter { var minBathNum: Int by filterDelegate(0) var minPrice: Float by filterDelegate(0f) ... }
  • 95.
    val filter =Filter() filter.addOnFilterChangedCallback(object: OnFilterCallback { override fun onFilterChanged(f: Filter) { println("Observer #1”) } }) filter.addOnFilterChangedCallback(object : OnFilterCallback { override fun onFilterChanged(f: Filter) { println("Observer #2") } }) filter.minBathNum = 2 filter.minPrice = 100_000f filter.minBathNum = 1 filter.minPrice = 10f old: 0 -> new: 2 Observer #1 Observer #2 old: 0.0 -> new: 100000.0 Observer #1 Observer #2 old: 2 -> new: 1 Observer #1 Observer #2 old: 100000.0 -> new: 10.0 Observer #1 Observer #2
  • 96.
    • Interface delegationfor composition. • lazy for… lazy initialization. • vetoable to define a validation to be reused. • observable if you need both old and new value. • Custom delegates for everything else. Delegates - Summary
  • 97.
    • Std Libhas a lot of functions to help you to write a more simple, readable and concise code. • Check it out the lib internals. You’ll learn a lot! • Check it out the Kotlin Koans (https://play.kotlinlang.org/ koans). It’s a good place to start (or recap)! • Write Kotlin code, enjoy the std lib and have fun! 😉 Wrap up!
  • 98.
    • Kotlin StandardFunctions
 https://medium.com/androiddevelopers/kotlin-standard-functions- cheat-sheet-27f032dd4326 • Mastering Kotlin standard functions: run, with, let, also and apply
 https://medium.com/@elye.project/mastering-kotlin-standard- functions-run-with-let-also-and-apply-9cd334b0ef84 • Java Friendly Kotlin
 https://codelabs.developers.google.com/codelabs/java-friendly-kotlin • Simpler Kotlin class hierarchies using class delegation
 https://proandroiddev.com/simpler-kotlin-class-hierarchies-using- class-delegation-35464106fed5 References
  • 99.