Download - K is for Kotlin
K is for Kotlin
Roman Ursu
Introduction
Kotlin is a JVM based language developed by JetBrains.
Kotlin was created with Java developers in mind and these are two very interesting features for Android developers:
● Kotlin is very intuitive and easy to learn for Java developers
● We have total integration with Android Studio
Introduction
● It’s more expressive (You can write more with much less code)
● It’s safer (Kotlin is null safe)
● It’s functional (lambda expressions, the way it deals with collections)
● It makes use of extension functions
● It’s highly interoperable (you can continue using most libraries and code written in Java, It’s even possible to create mixed projects)
Few facts
● Compatibility: Kotlin is fully compatible with JDK 6, fully supported in Android Studio and compatible with the Android build system.
● Performance: A Kotlin application runs as fast as an equivalent Java one, thanks to very similar bytecode structure.
● Interoperability: Kotlin is 100% interoperable with Java, allowing to use all existing Android libraries in a Kotlin application.
● Footprint: Kotlin has a very compact runtime library. Kotlin runtime adds only a few hundred methods.
● Compilation Time: Kotlin supports efficient incremental compilation.
Configuring Gradle
buildscript {
ext.kotlin_version = '1.1.3-2'
ext.support_version = '25.3.1'
ext.anko_version = '0.9'
repositories {
jcenter()
maven {
url 'https://maven.google.com'
}
}
dependencies {
classpath 'com.android.tools.build:gradle:3.0.0-alpha7'
classpath "org.jetbrains.kotlin:kotlin-gradle-
plugin:$kotlin_version"
}
}
...
apply plugin: 'com.android.application'
apply plugin: 'kotlin-android'
apply plugin: 'kotlin-android-extensions'
apply plugin: 'kotlin-kapt'
Android Studio Support
Converting Java code to Kotlin.public class KotlinExampleApp extends Application {
public static KotlinExampleApp app;
private AppComponent appComponent;
@Override
public void onCreate() {
super.onCreate();
app = KotlinExampleApp.this;
appComponent = DaggerAppComponent.builder()
.appModule(new AppModule(this))
.netModule(new NetModule())
.weatherModule(new WeatherModule())
.build();
}
public AppComponent getAppComponent() {
return appComponent;
}
}
class KotlinExampleApp : Application() {
var appComponent: AppComponent? = null
private set
companion object {
var app: KotlinExampleApp
}
override fun onCreate() {
super.onCreate()
app = this@KotlinExampleApp
appComponent =
DaggerAppComponent.builder()
.appModule(AppModule(this))
.netModule(NetModule())
.weatherModule(WeatherModule())
.build()
}
}
class KotlinExampleApp : Application() {
var appComponent: AppComponent? = null
private set
companion object {
var app: KotlinExampleApp? = null
}
override fun onCreate() {
super.onCreate()
app = this@KotlinExampleApp
appComponent = DaggerAppComponent.builder()
.appModule(AppModule(this))
.netModule(NetModule())
.weatherModule(WeatherModule())
.build()
}
}
Classes declaration
By default, a class always extends from Any (similar to Java Object), but we can extend any other classes.
open class Animal(name: String)
class Person(firstName: String, lastName: String) : Animal(firstName)
class Customer(val customerName: String = "defaultName") // default value
Instantiation
val customer = Customer("Joe Smith")
Constructorsclass Person constructor(firstName: String) {}
class Person(firstName: String) {}
class Customer(name: String) {init {
logger.info("Customer initialized with value ${name}")}
}
class Customer(name: String) {val customerKey = name.toUpperCase()
}
class Person(val firstName: String, vallastName: String, var age: Int) { }
class Person(val name: String) {constructor(name: String, parent: Person) :
this(name) {parent.children.add(this)
}}
Inheritance (Overriding Methods)
open class Base {open fun v() {}fun nv() {}
}
class Derived() : Base() {override fun v() {}
}
open class AnotherDerived() : Base() {final override fun v() {}
}
Inheritance
open class A {open fun f() { print("A") }fun a() { print("a") }
}
interface B {fun f() { print("B") } // interface members are 'open' by defaultfun b() { print("b") }
}
class C() : A(), B {// The compiler requires f() to be overridden:override fun f() {
super<A>.f() // call to A.f()super<B>.f() // call to B.f()
}}
“Static methods”
There are No static methods in Kotlin.In most cases, it's recommended to simply use package-level functions instead. Companion object inside your class will make you able to call its members with the same syntax as calling static methods in Java.
class MyClass {
companion object Factory {
fun create(): MyClass = MyClass()
}
}
...
val instance = MyClass.create()
Basic Types
● Basic types such as integers, floats, characters or booleans still exist, but they all act as an object
● There are no automatic conversions among numeric types
● Characters (Char) cannot directly be used as numbers. (use *.toInt())
● Bitwise arithmetical operations are a bit different. (“and” instead of “&”, “or”instead of “|”)
● It's common practice in Kotlin is to omit variable types
● A String can be accessed as an array and can be iterated
Variables and Constants
var str = "String" // A String
var number = 25 //An Int
val a: Int = 23
val c: Context = activity
Properties
public class Person {
var name: String = ""
get() = field.toUpperCase()
private set(value) {
field = "Name: $value"
}
//...
}
Methods
private fun method1(a: Int): Int {return a * a
}
private fun method2(a: Int): Int = a * a
private fun method3(a: Int) = a * a
private fun doSmth(): Unit {/* nothing here */}
Extension functions
● adds a new behaviour to a class
● even if we don’t have access to the source code
● we don’t need to pass the object as an argument
● we can implement it using this and all its public methods
fun Context.toast(message: CharSequence, duration: Int = Toast.LENGTH_SHORT) {Toast.makeText(this, message, duration).show()
}
Usagetoast("Hello world!")toast("Hello world!", Toast.LENGTH_LONG)
Data Classes
● Avoid the boilerplate we need in Java to create POJO
● They usually only provide plain getters and setters to access to their fields
● We get equals(), hashCode(), copy() for free
data class Forecast(@SerializedName("date") val date: Date,@SerializedName("temp") val temperature: Float,@SerializedName("desc") val description: String) // that’s all the declaration
// copy but change smthval f1 = Forecast(Date(), 27.5f, "Storming")val f2 = f1.copy(temperature = 31f)
Declaration Destructuring
val f1 = Forecast(Date(), 27.7f, "Shiny day")val (date, temperature, details) = f1
...val otherVariable = dateval otherTemperature = temperature
for ((key, value) in map) {Log.d("map", "key:$key, value:$value")
}
With
With allow you to use public functions and properties without specifying variable.
override fun onBindViewHolder(holder: ViewHolder?, position: Int) {
with(items[position]) { // WeatherDataDto object contains props temperature, humidity etc
holder!!.temperature!!.text = temperature.toString()
holder.humidity!!.text = humidity.toString()
holder.description!!.text = description
holder.windSpeed!!.text = windSpeed.toString()
Glide.with(this@MainActivity).load(iconUrl).into(holder.icon)
}
holder!!.itemView.setOnClickListener { itemClick(items[position]) }
}
Lambdas
Lambda expression is a simple way to define an anonymous function. Function that receives an interface with a single function can be substituted by a lambda.
Beforepublic interface OnClickListener { void onClick(View v) }...view.setOnClickListener(new OnClickListener() {public void onClick(View v) {Toast.makeText(v.getContext(), "Click", Toast.LENGTH_SHORT).show();
} });Now
fun setOnClickListener(listener: (View) -> Unit)...view.setOnClickListener({ view -> toast("Click")})
// can be simplified toview.setOnClickListener { toast("Click") }
Collections
ImmutableIterableCollectionListSetMap
MutableMutableIterableMutableCollectionMutableListMutableSetMutableMap
We can use Java collections, but Kotlin provides a good set of native interfaces:
Functional operationsany (Returns true if at least one element matches the given predicate)val list = listOf(1, 2, 3, 4, 5, 6)assertTrue(list.any { it % 2 == 0 })
all (Returns true if all the elements match the given predicate)assertTrue(list.all { it < 10 })
count (Returns the number of elements matching the given predicate)assertEquals(3, list.count { it % 2 == 0 })
fold (Accumulates the value starting with an initial value and applying an operation from the first to the last element in a collection)assertEquals(25, list.fold(4) { total, next -> total + next })
forEachIndexed (Same as forEach, though we also get the index of the element)list.forEachIndexed { index, value -> println("position $index contains a $value") }
Functional operationsdropWhileReturns a list containing all elements except first elements that satisfy the given predicate.assertEquals(listOf(3, 4, 5, 6), list.dropWhile { it < 3 })
filterReturns a list containing all elements matching the given predicate.assertEquals(listOf(2, 4, 6), list.filter { it % 2 == 0 })
takeReturns a list containing first n elements.assertEquals(listOf(1, 2), list.take(2))
groupByReturns a map of the elements in original collection grouped by the result of given functionassertEquals(mapOf("odd" to listOf(1, 3, 5), "even" to listOf(2, 4, 6)), list.groupBy { if (it % 2 == 0) "even"else "odd" })
Functional operationselementAtOrNullReturns an element at the given index or null if the index is out of bounds of this collection.assertNull(list.elementAtOrNull(10))
sortByReturns a list of all elements, sorted by the specified comparator.assertEquals(listOf(3, 7, 2, 5), unsortedList.sortBy { it % 3 })
plusReturns a list containing all elements of the original collection and then all elements of the given collection. Because of the name of the function, we can use the ‘+’ operator with it.assertEquals(listOf(1, 2, 3, 4, 5, 6, 7, 8), list + listOf(7, 8))
And many more
Null safety in Kotlin
Being null considered the billion-dollar mistake by its own creator (Tony Hoare)
// this is Java and it will compile
Forecast forecast = null;forecast.toString();
// this is Kotlin and it won’t compile
val forecast: Forecast? = nullforecast.toString()
// use !! if you a sure that this can’t be nullforecast!!.toString();
// use ?. if you hesitateforecast?.toString();
Lazy
If you don’t want to declare variable as nullable but you can’t initialize it in constructor use lateinit
class App : Application() {companion object {
lateinit var instance: App}
overrride fun onCreate() {super.onCreate()instance = this
}}
Flow control (IF)
In Kotlin, if is an expression, i.e. it returns a value.
if branches can be blocks, and the last expression is the value of a block:
val max = if (a > b) {print("Choose a")a
} else {print("Choose b")b
}
Flow control (WHEN)
when replaces the switch operator of C-like languages. In the simplest form
when (x) {
1 -> print("x == 1")
2 -> print("x == 2")
else -> {
print("x is neither 1 nor 2")
}
}
when {
x.isOdd() -> print("odd")
x.isEven() -> print("even")
else -> print("x is weird")
}
when (x) {
in 1..10 -> print("x is in the range")
in validNumbers -> print("x is valid")
!in 10..20 -> print("outside the range")
else -> print("none of the above")
}
For Loops
for iterates through anything that provides an iterator
for (item in collection) print(item)
for (i in array.indices) {print(array[i])
}
for ((index, value) in array.withIndex()) {println("at $index is $value")
}
Inconveniences
Code completion (variables declaration)
Java caseYou need only type class name and variable name comes by itself :)
Kotlin caseYou should type everything :(
Inconveniences
Code completion (in RxJava)
Java case
Inconveniences
Code completion (in RxJava)
Kotlin case
I need some hint... and I don’t want to search what T means!!! >:(
Thank you for attention