Adding Kotlin
to existing
Android project
Anton Rutkevich
JunoLab
GDG Minsk
About me
- Use Java, Groovy, Kotlin
- For Android,
Google App Engine
Who's there? :)
Why?
What?
How?
Why?
More code -
more bugs
- No lambdas (unless you use Java 8)
- No properties; much boilerplate (Lombok)
- String concatenation
- *Utils classes
Easy to write
non-safe code
- non-final by default
- nullable by default
- No 'module' access level
- ...
What?
Main Kotlin goals
- To create a Java-compatible language,
- That compiles at least as fast as Java,
- Make it safer than Java
- Make it more concise than Java
- And make it way simpler than the most mature competitor – Scala.
Basics
Variables
/* Read-only */
val a: Int = 1
// `Int` type is inferred
val b = 1
// Type required when no initializer is provided
val c: Int
/* Read-write */
var x = 5 // `Int` type is inferred
x += 1
Functions
fun sum(a: Int, b: Int): Int {
return a + b
}
fun sum(a: Int, b: Int): Int = a + b
when
fun cases(obj: Any) {
when (obj) {
1 -> print("One")
"Hello" -> print("Greeting")
is Long -> print("Long")
!is String -> print("Not a string")
else -> print("Unknown")
}
}
Classes
Constructors
class Invoice
class Customer(val name: String)
class Dog(name: String) {
init {
logger.info("Initialized with ${name}")
}
}
class MyView : View {
constructor(ctx: Context) : super(ctx) {
}
constructor(ctx: Context, attrs: AttributeSet)
: super(ctx, attrs) {
}
}
Inheritance
open class Base {
open fun canBeOverridden() {}
fun notOverrideable() {}
}
class Derived() : Base() {
override fun canBeOverridden() {}
}
No statics
class C {
companion object {
fun create() = C()
}
}
fun main() {
// C denotes the companion object here
val c = C.create()
// can be a value
val cCompanionObject = C.Companion
val cObject: C = cCompanionObject.create();
}
Nested & Inner class
class Outer {
private val bar: Int = 1
class Nested {
fun foo() = 2
}
}
class Outer {
private val bar: Int = 1
inner class Inner {
fun foo() = bar
}
}
Visibility modifiers
- private — visible only in the declaring scope and its subscopes (inside the same module);
- protected — (applicable only to class/trait members) like private, but also visible in subclasses;
- internal — (used by default) visible everywhere within the same module (if the owner of declaring scope is visible);
- public — visible everywhere (if the owner of declaring scope is visible).
Properties
var <propertyName>: <PropertyType> [= <property_initializer>]
<getter>
<setter>
public class Address {
public var name: String = ...
var stringRepresentation: String
get() = this.toString()
set(value) {
// parses string into this object
setDataFromString(value)
}
}
Backing fields
// the initializer value is written
// directly to the backing field
var counter = 0
set(value) {
if (value >= 0)
$counter = value
}
// no backing field
val isEmpty: Boolean
get() = this.size == 0
Backing properties
Null-safety
var a: String = "abc"
a = null // compilation error
var b: String? = "abc"
b = null // ok
val l = a.length() // valid
val m = b.length() // error: variable 'b' can be null
// safe casts
b?.length()
// Elvis!
val n = b?.length() ?: -1
Advanced
Extensions
fun MutableList<Int>.swap(x: Int, y: Int) {
// 'this' corresponds to the list
val tmp = this[x]
this[x] = this[y]
this[y] = tmp
}
val l = arrayListOf(1, 2, 3)
// 'this' inside 'swap()' will hold the value of 'l'
l.swap(0, 2)
Resolved statically!
Lambdas
// function that takes lambda
fun synchronized(lock: Lock, body: () -> Unit): Unit {
lock.lock()
try {
body()
} finally {
lock.unlock()
}
}
// usage
synchronized (lock) {
doSomething()
}
Lambdas 2
fun <T, R> List<T>.map(transform: (T) -> R): List<R> {
val result = ArrayList<R>()
for (item in this)
result.add(transform(item))
return result
}
//usage
val ints = listOf(1, 2, 3)
val doubled = ints.map {it -> it * 2}
// also valid
val strings = listOf("one", "three", "five", "seven")
val transformed = strings.filter {it.length == 5}
.sortBy {it}.map {it.toUpperCase()}
Inline functions
// inline function
inline fun synchronized(lock: Lock, body: () -> Unit): Unit {
...
}
// the code ..
synchronized(lock) { foo() }
// .. will be transformed to
lock.lock()
try {
foo()
} finally {
lock.unlock()
}
Other cool features
- Ranges
- Type-safe builders
- Delegation
- Data classes
- Multi-declarations
- ...
Shut up and take my money method count*
Library | Jar Size | Method Count |
---|---|---|
kotlin-runtime-0.11.91.4 | 363 KB | 952 |
kotlin-stdlib-0.11.91.4 | 656 KB | 6142 |
scala-library-2.11.5 | 5.3 MB | 50801 |
groovy-2.4.0-grooid | 4.5 MB | 29636 |
guava-18.0 | 2.2 MB | 14833 |
* Based on Jake Wharton's research (see links), updated
How?
Install plugin
Add Kotlin to project
Convert Java to Kotlin :)
Or create Kotlin files :)
Java Interop
- Smooth in both directions
- Provides type mapping
- Nulls are the most dangerous part !
Links
- This presentation:
https://slides.com/antonrutkevich/adding-kotlin-to-existing-android-project - Kotlin
http://kotlinlang.org/ - Kotlin study workshop
http://kotlinlang.org/docs/tutorials/koans.html
anton.rutkevich@gmail.com