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