Kotlin
Impressions and conclusions
after year on production
Contens
- Why Kotlin?
- Few Examples
- Classes and properties
- Typing in Kotlin
- Functions and Lambdas
- Extension Functions
- Conclusions
Things I won't tell You about
- Functional programming in Kotlin
- What the hell are Delegates?
- DSL in Kotlin
I wanted, but they deserve for a separate presentation
Why Kotlin?
Java is dead
Java is slowly collapsing giant
Text
Text
Text
Text
Google Search and Book Selling is indicator of what people are interested in
Why Kotlin?
There is so much mature JVM programming languages
Why Kotlin?
Both Scala and Groovy limp on:
- Compilation time
- Execution time
- Java compability
They are fun, but not pragmatic
Java compatibility
Cmd + Shift + A
Ctrl + Shift + A
Configure Kotlin in the project
Cmd + Shift + A
Ctrl + Shift + A
Convert Java File to Kotlin File
Kotlin
Kotlin have most of the modern languages features (and some custom), while keeping build and execution time close Java.
Few examples
Hello World
fun main(args: Array<String>) {
println("Hello world")
}
Iteration
for (i in 1..10) {
print(i)
}
// 12345678910
for (i in 10 downTo 1 step 2) {
print(i)
}
// 108642
for (c in 'A'..'Z') {
print(c)
}
// ABCDEFGHIJKLMNOPQRSTUVWXYZ
for (c in "Some text") {
print("$c.")
}
// S.o.m.e. .t.e.x.t.
Iteration
val capitolToCountry = listOf(
"Washington" to "United States of America",
"Warsaw" to "Poland",
"London" to "Great Britan"
)
for ((capitol, country) in capitolToCountry) {
println("Capitol of $country is $capitol")
}
// Capitol of United States of America is Washington
// Capitol of Poland is Warsaw
// Capitol of Great Britan is London
Iteration
listOf(User("Marcin", "Moskala"), User("Michal", "Michalski"), User("Ania", "Mania"))
.filter { it.name.startsWith("M") }
.map { (name, surname) -> "$name $surname" }
.joinToString(separator = " and ")
.let(::print)
Iteration
val s = (1..10) // Range from 1 to 10
.filter { it % 2 == 0 } // [2, 4, 6, ...]
.map { it * it } // [4, 16, 36, ...]
.sum() // 220
Quick Sort
fun <T : Comparable<T>> List<T>.quickSort(): List<T> {
if (size < 2) return this
val pivot = first()
val (smaller, greater) = subList(1, size).partition { it <= pivot }
return smaller.quickSort() + pivot + greater.quickSort()
}
Conclusions
Short
Good-looking
Minimal syntax
Lot's of possibilities
Null safety &
Read-only
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.
Read-only properties
val name: String = "Marcin"
Read only
var name: String = "Marcin"
Changeable
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.surname == "Bąk")
Elvis operator
person?.name ?: "unknown"
val name = person?.name ?: throw Error("No person or name is null")
val name = person?.name ?: return
Conclusion
Null safety prevents from NPE - most common Java error.
Null safety makes Kotlin code much safer while it cost nearly nothing. It is Simple and smart.
One catch - when we assume that some value form API or Java lib is not-null and we will have case where it is then we have an exception even if we don't use it.
Null safety promotes val usage over var, because in val there is no Smart Casting in case of multithreading.
Kotlin often promotes using val over var. It is good for thread safety and also really good practice.
Classes and properties
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(val name: String, val surname: String, val age: Int)
Yap, this is exactly the same
Getters and setters are there
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
Conclusion
Kotlin syntax for defining classes is zero-boilerplate.
Kotlin properties are much more powerful then Java fields.
This leads to short, elegant and easily changeable code.
Typeing in Kotlin
Type system
val name = "Marcin"
val name: String = "Marcin"
listOf(1, 2, 3, 4, 5, 6).filter { it > 3 }.map { it * 2 }.sum()
val double: (Int)->Int = { i -> i * 2 }
val double = { i: Int -> i * 2 }
Smart Casting
Conclusion
Having static typing and good inference system, we can achieve all benefits from static typing while getting most of benefits form dynamic.
Functions
Functions
Single-expression function
Type is inferred
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 main(args: Array<String>) {
var a: Int = 0
val inc = { a++ }
val dec = { a--}
val prt = { println(a) }
button1.onClick = { inc(); prt() }
button2.onClick = { dec(); prt() }
}
Lambdas (closures in fact)
fun main(args: Array<String>) {
var a: Int = 0
button1.onClick = { print(a++) }
button2.onClick = { print(a--) }
}
Higher order functions
fun getCounter(): ()->Int {
var i: Int = 0
return { i++ }
}
Java SAM to Kotlin function
// Java
interface OnClickListener {
fun onClick(v: View)
}
// Java
View.OnClickListener listener = new View.OnClickListener() {
@Override
public void onClick(View v) {
// Code
}
};
val listener = View.OnClickListener { /* Code */ }
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") }
Conclusion
Kotlin allows fully functional programming.
It's function syntax it short and simple.
Kotlin lambdas not only shorter, but also much more powerful then Java anonymous objects or Java 8 lambdas.
Kotlin made a bridge to simply use Java methods with function interfaces.
Whole function system makes Kotlin syntax shorter and more readable. It also prevents from redundancy.
Extension
functions
Java Utils hell
Java Utils hell
StringUtils.capitalize(str)
val replacedStr = StringUtils.replaceAll(str, "name" to name, "surname" to surname)
val capitalizedStr = StringUtils.capitalize(replacedStr)
Kotlin extension functions
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 functions
in practice
view.visibility = Visible.GONE
ViewUtlis.hide(view)
We often do
No better
view.hide()
ViewUtlis.hide(view)
Better
Good looking
Less redundant
Simple
Kotlin extension properties
val Context.appVersion: String
get() = packageManager.getPackageInfo(packageName, 0).versionName
var View.isVisible: Boolean
get() = visibility == View.VISIBLE
set(value) {
visibility = if (value) View.VISIBLE else View.GONE
}
Conclusion
Kotlin Extension Functions lead to good looking, less redundant and simple code.
They allow to extend libraries with own functionalities.
It is dangerous - each project have it's own extension functions, so it is leading to different programming style.
Final conclusions
Philosophy of Kotlin
- Pragmatic
-
Concise
-
Safe
-
Interoperable
Pragmatic
Kotlin is a practical language designed to solve real-world problems.
Kotlin is based on years of industry experience.
Kotlin is shifting lot's of responsibilities from programmer to IDE.
Concise
- Zero boilerplate
- Lot's of ways to omit redundancy
- Promotes making reusable code
- Even Java functions are shorted
Kotlin syntax is minimal
- Minimizes redundancy
- Allow funcion programming
Kotlin syntax is easily changeable
Safe
- Null safety
- Statically typed while smart casted
- Promotes read only values
- Promotes immutable types
Kotlin syntax is safe
Interoperable
Kotlin is perfectly working with Java APIs, tools and libraries. Also Kotlin can be always used from Java. It is keeping all that Java gives, while adding it's own features.
Kotlin
By Marcin Moskala
Kotlin
- 733