Kotlin

Next level of Android Development

Safeness

Language safeness - more errors can be prevented by checks at compile time instead of falling at run time. Also language is referred as safe when it is promoting way of coding that is know as more secure.

Billion-dollar mistake

Sir Charles Hoare

"This has led to innumerable errors, vulnerabilities, and system crashes, which have probably caused a billion dollars of pain and damage in the last forty years."

Null safety

var person: Person

Must be inilialized

var person: Person = null

Not null type cannot be null

var person: Person? = null

? after type makes it nullable

person.name

Nullable must be unpacked

Null safety

person?.name

Safe call (null if person is null)

person!!.name

Unsafe call (exception if person is null)

if(person != null) {
    person.name
}

Smart Cast (after nullability check, object is casted to not-null)

if(person == null)
    return

person.name
if(person != null && person.name == "Marcin")
if(person == null || person.age < 18)

Elvis operator

person?.name ?: "unknown"
val name = person?.name ?: throw Error("No person or name is null")
val name = person?.name ?: return

Read-only properties

val name: String = "Marcin"

Read only

var name: String = "Marcin"

Read-write

val list = listOf("A", "B", "C")
val name: String by lazy { getNameFromForm() }

Read-only properties

MutableList

Mutable

List

Immutable

var list = listOf("A", "B", "C")
list += "D"
list -= "A"
val list = mutableListOf("A", "B", "C")
list.add("D")
list.remove("A")

Conciseness and readability

Classes

// Java
public class PersonJava {
   private String name;
   private String surname;
   private int age;

   PersonJava(String name, String surname, int age) {
       this.setName(name);
       this.setSurname(surname);
       this.setAge(age);
   }

   public String getName() {
       return name;
   }

   public void setName(String name) {
       this.name = name;
   }

   public String getSurname() {
       return surname;
   }

   public void setSurname(String surname) {
       this.surname = surname;
   }

   public int getAge() {
       return age;
   }

   public void setAge(int age) {
       this.age = age;
   }
}

Classes

class Person {
    var name: String
    var surname: String
    var age: Int

    constructor(name: String, surname: String, age: Int) {
        this.name = name
        this.surname = surname
        this.age = age
    }
}

Yap, this is exactly the same

Getters and setters are there

Classes

class Person(name: String, surname: String, age: Int) {
    var name: String = name
    var surname: String = surname
    var age: Int = age
}

Still the same

Primary constructor

Classes

class Person(
    var name: String, 
    var surname: String, 
    var age: Int
)

Classes

// Kotlin
class Person(
    var name: String, 
    var surname: String, 
    var age: Int
)
// Java
public class PersonJava {
   private String name;
   private String surname;
   private int age;

   PersonJava(String name, String surname, int age) {
       this.setName(name);
       this.setSurname(surname);
       this.setAge(age);
   }

   public String getName() {
       return name;
   }

   public void setName(String name) {
       this.name = name;
   }

   public String getSurname() {
       return surname;
   }

   public void setSurname(String surname) {
       this.surname = surname;
   }

   public int getAge() {
       return age;
   }

   public void setAge(int age) {
       this.age = age;
   }
}

Classes

// Kotlin
data class Person(
    var name: String, 
    var surname: String, 
    var age: Int
)
// Java
public class PersonJava {
    private String name;
    private String surname;
    private int age;

    PersonJava(String name, String surname, int age) {
        this.setName(name);
        this.setSurname(surname);
        this.setAge(age);
    }

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

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

        PersonJava that = (PersonJava) o;

        if (age != that.age) return false;
        if (name != null ? !name.equals(that.name) : that.name != null) return false;
        return surname != null ? surname.equals(that.surname) : that.surname == null;
    }

    @Override
    public int hashCode() {
        int result = name != null ? name.hashCode() : 0;
        result = 31 * result + (surname != null ? surname.hashCode() : 0);
        result = 31 * result + age;
        return result;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public String getSurname() {
        return surname;
    }

    public void setSurname(String surname) {
        this.surname = surname;
    }

    public int getAge() {
        return age;
    }

    public void setAge(int age) {
        this.age = age;
    }
}
person.toString() // Person { name = "Marcin, ...

person1 == person2 // Checks equal of each field

person.copy(surname = "Moskała") 
// New object with only chosen properties changed

Properties

var name: String? = null
    get() = name?.capitalize()
    set(value) {
        if(value == null || value.length == 0) return
        field = value
    }

We can still edit getters and setters

Kotlin property = Java private field + accessors

accessors = getter for var, getter + setter for var

Properties

class Person(val name: String, val surname: String) {
    val fullName: String
        get() = "$name $surname"
}

Here Kotlin property is just a setter

Reusability

Java Utils hell

Java Utils hell

view.visibility = View.GONE
view.hide()
ViewUtils.hide(view)

Java Utils hell

StringUtils.capitalize(str)
val replacedStr = StringUtils.replaceAll(str, "name" to name, "surname" to surname)
val capitalizedStr = StringUtils.capitalize(replacedStr)
str.capitalize()
str.replaceAll("{name}" to name, "{surname}" to surname)
    .capitalize()

Kotlin extension functions

fun String.noLongerThen(max: Int): String {
    return this.substring(0, Math.min(max, this.length))
}

fun main(args: Array<String>) {
    println("Joe".noLongerThen(4)) // Joe
    println("James".noLongerThen(4)) // Jame
    println("Ashley".noLongerThen(4)) // Ashl
}

Kotlin extension functions

Toast.makeText(this, text, Toast.LENGTH_SHORT).show();
fun Context.toast(text: String) {
    Toast.makeText(this, text, Toast.LENGTH_SHORT).show();
}
toast(text)

Kotlin extension properties

val Context.inflater
   get() = getSystemService(Context.LAYOUT_INFLATER_SERVICE) as LayoutInflater
var View.isVisible: Boolean
    get() = visibility == View.VISIBLE
    set(value) {
        visibility = if (value) View.VISIBLE else View.GONE
    }
context.inflater
progress.isVisible = true

Functions everywhere

fun double(i: Int) = i * 2

class A() {

    fun triple(i: Int) = i * 3

    fun twelveTimes(i: Int): Int {
        fun fourTimes(i: Int) = double(double(i))
        return triple(fourTimes(i))
    }
}


fun main(args: Array<String>) {
    double(1) // 2
    A().twelveTimes(2) // 24
}

Top-level function

Mamber function

Local function

Lambdas (closures in fact)

fun setUpCounter() {
    var a: Int = 0
    fun showValue() {
        counterView.text = "$a"
    }
    counterPlusView.setOnClickListener { a++; showValue() }
    counterPlusView.setOnClickListener { a--; showValue() }
}

Java SAM parameter to Kotlin function parameter

// Java
button.setOnClickListener(new View.OnClickListener() {
    @Override
    public void onClick(View v) {
        Toast.makeText(getBaseContext(), "Button clicked", Toast.LENGTH_LONG).show();
    }
});
button.setOnClickListener { toast("Button clicked") }

Collection stream processing

students.filter { it.passing }
        .sortedBy { it.grade }
        .take(10)
        .joinToString(
                prefix = "Best 10 students are:\n",
                separator = "\n",
                transform = { (name, surname) -> "$name $surname" }
        )

Collection stream processing

inline fun <T, R> Iterable<T>.map(transform: (T) -> R): List<R> {
   val destination = ArrayList<R>()
   for (item in this) destination.add(transform(item))
   return destination
}

inline fun <T> Iterable<T>.filter(predicate: (T) -> Boolean): List<T> {
   val destination = ArrayList<T>()
   for (item in this) if(predicate(item)) destination.add(item)
   return destination
}

Collection stream processing

"This is Sparta".map { it.toUpperCase() }.toString() // THIS IS SPARTA

Expressability

Single-expression function

Type is inferred

Expressions

Everything is an expression

Functional programming

fun <T : Comparable<T>> List<T>.quickSort(): List<T> = when {
    size <= 1 -> this
    else -> {
        val pivot = first()
        val (smaller, greater) = drop(1).partition { it <= pivot }
        smaller.quickSort() + pivot + greater.quickSort()
    }
}

Functional programming

Powerset in mathematics is set of all possible subsets of this set, including this set and empty set.

powerset({}) == {{}}

powerset({1}) == {{}, {1}}

powerset({1, 2}) == {{},  {1},  {2}, {1, 2}}

Functional programming

fun <T> Collection<T>.powerset(): Set<Set<T>> = when {
    isEmpty() -> setOf(setOf())
    else -> drop(1).powerset().let { it + it.map { it + first() } }
}

powerset({1, 2}) = powerset({2}) + powerset({2}).map { it + 1 }

powerset({2}) = powerset({}) + powerset({}).map { it + 2 }

powerset({}) = {{}}

powerset((2)) = {{}} + {{2}} = {{}, {2}}

powerset({1, 2}) = {{}, {2}} + {{1}, {1, 2}} = {{}, {1}, {2}, {1, 2}}

Functional programming

fun <T> Collection<T>.powerset(): Set<Set<T>> = powerset(this, setOf(setOf()))

private tailrec fun <T> powerset(left: Collection<T>, acc: Set<Set<T>>): Set<Set<T>> = when {
    left.isEmpty() -> acc
    else ->powerset(left.drop(1), acc + acc.map { it + left.first() })
}

What I skipped?

  • Property Delegation
  • Class Delegation
  • Kotlin DSL
  • Function Literals with Receiver

  • Multi-platform programming in Kotlin

Used photos

kotlinlang.com

https://blog.jetbrains.com/kotlin/2017/05/kotlin-on-android-now-official/

https://blog.gradle.org/kotlin-meets-gradle

https://developer.android.com/kotlin/index.html

http://www.opinie.senior.pl/zdjecia/Sosy-majonezy-musztardy/Ketchup-0-dodatku-cukru-30378-big.png

https://medium.com/@giuliani.arnaud/kotlin-in-production-one-year-later-d6d3b66f870c

http://2.bp.blogspot.com/-46cd0AT4Dwo/VmuhPnUH1iI/AAAAAAAAMDs/m8HX2l39mks/s1600/cropped-chiquita-dm2-minion-dave-bananas.jpg

http://dobsondev.com/2014/06/06/the-elvis-operator/

Kotlin

Next level of Android Development

Kotlin: Next level of Android Development

By Marcin Moskala

Kotlin: Next level of Android Development

How Kotlin makes Android development extremely more effective.

  • 1,164