kotlin advanced - language reference for android developers

36
Kotlin lang - advanced (Android projects) Bartosz Kosarzycki - StxNext Lightning Talks - Mar 11, 2016 talented developers | flexible teams | agile experts

Upload: stx-next

Post on 15-Apr-2017

436 views

Category:

Technology


1 download

TRANSCRIPT

Page 1: Kotlin Advanced - language reference for Android developers

Kotlin lang - advanced(Android projects)

Bartosz Kosarzycki - StxNext Lightning Talks - Mar 11, 2016

talented developers | flexible teams | agile experts

Page 3: Kotlin Advanced - language reference for Android developers

Kotlin lang - advanced1. Live templates2. Enum translation3. Calling extension functions from Kotlin/Java4. Constructors with backing fields5. Warnings6. F-bound polymorphism7. Variance (Covariance/Contravariance)8. Variance comparison in Kotlin/Java/Scala9. Annotation processing - KAPT

10. SAM conversions11. Type equality12. Lambda vs Closure13. Reified generics14. Fluent interfaces15. Infix notation16. Static extension methods in Kotlin17. Generic types18. Sealed classes19. Dokka - documentation in Kotlin20. J2K converter21. Real-world example22. Reflection

AGENDA

Page 4: Kotlin Advanced - language reference for Android developers

Live templates

exfunfun Any.f(): Unit { }

closure{ x -> x }

iterfor (i in iterable) { }

● let’s start with something simple - templates

Page 5: Kotlin Advanced - language reference for Android developers

Live templates

exvarvar Any.v: Any get() { } set(value) {

}

mainfun main(args: Array<String>) { }

innif (i != null) { }

Page 6: Kotlin Advanced - language reference for Android developers

Enum translation

Kotlinenum class SliderActivityType private constructor(val title: Int) { PORTFOLIO(R.string.portfolio), TEAM(R.string.team)}

Javapublic enum SliderActivityType {

PORTFOLIO(R.string.portfolio), TEAM(R.string.team);

private final int title;

SliderActivityType(int title) { this.title = title; }

public int getTitle() { return title; }}

● easier to read● more concise● help to avoid typos and boilerplate code

In Kotlin translated enums are:

Page 7: Kotlin Advanced - language reference for Android developers

Calling extension

functions

Get app-version ext. function:@file:JvmName("ActivityUtil") //@file:JvmMultifileClasspackage com.stxnext.stxinsider.util

fun Activity.getAppVersion(activity: Activity): String { try { val manager = activity.packageManager val info = manager.getPackageInfo(activity.packageName, 0).versionName } catch (e: PackageManager.NameNotFoundException) { /* ignore */ } return "0.0.0"}

Kotlin call:versionTextView.setText(getAppVersion(this))

Java call:versionTextView.setText(ActivityUtil.getAppVersion(MainActivity.this, MainActivity.this));

Page 8: Kotlin Advanced - language reference for Android developers

Constructors with

backing fields ● using constructors with backing fields in

a proper way saves a lot of boiler-plate code

“Java-style” kotlin code:

class TeamCategoryFragment : Fragment() {

internal val TAG = TeamCategoryFragment::class.simpleName lateinit var teamCategoryHeader: TeamCategoryHeader lateinit var teamListRecyclerView: RecyclerView

fun teamCategoryHeader (teamCategoryHeader: TeamCategoryHeader): TeamCategoryFragment { this.teamCategoryHeader = teamCategoryHeader return this }

override fun onCreateView(): View? { val view = inflater!!.inflate(R.layout.fragment_layout, container, false) teamListRecyclerView = view.findViewById(R.id.fragment_list) as RecyclerView return view; }}

class TeamCategoryFragment (var teamCategoryHeader: TeamCategoryHeader) : Fragment() {

internal val TAG = TeamCategoryFragment::class.simpleName lateinit var teamListRecyclerView: RecyclerView override fun onCreateView(): View? { val view = inflater!!.inflate(R.layout.fragment_layout, container, false) teamListRecyclerView = view.findViewById(R.id.fragment_list) as RecyclerView return view; }}

Contructor with backing field

Page 9: Kotlin Advanced - language reference for Android developers

Warnings

Checks:

Full list of supression contants:

https://github.com/JetBrains/kotlin/blob/master/compiler/frontend/src/org/jetbrains/kotlin/diagnostics/rendering/DefaultErrorMessages.java

@Suppress("UNCHECKED_CAST")@Suppress("CANNOT_CHECK_FOR_ERASED")@Suppress("SENSELESS_COMPARISON")@Suppress("GENERIC_THROWABLE_SUBCLASS")etc.

Page 10: Kotlin Advanced - language reference for Android developers

F-bound polymorphism

Kotlin:

interface Movable { Movable move(int x, int y);}class Car implements Movable { @Override public Movable move(int x, int y) { return this; }}

public class FBoundedExample {

Movable moveMeOneInch (Movable m) { return m.move(1,1); } <T extends Movable > T moveMeOneInchFBound (T m) { m.move( 1,1); return m; }

Car car1 = (Car) moveMeOneInch( new Car()); Car car2 = moveMeOneInchFBound( new Car());}

Java equivalent:

interface Movable { fun move(x: Int, y: Int): Movable { return this }}class Car : Movable

fun moveMeOneInch(m: Movable): Movable { return m.move(1, 1)}

fun <T : Movable> moveMeOneInchFBound(m: T): T { m.move(1, 1) return m}

val car1:Car = moveMeOneInch(Car()) as Carval car2:Car = moveMeOneInchFBound(Car())

● in moveMeOneInch() we have to do unsafe casting to (Car)

● in moveMeOneInchFBound() no casting in necessary

● Kotlin&Java work the same but the syntax is differrent (<T : Movable> vs <T extends Movable>

● upper-bound generics

no casting necessary

Page 11: Kotlin Advanced - language reference for Android developers

Covariance

Notes:

● Cat is a subclass of Animal

● Animal shelter puts animals and

gets them for adoption

● We want to implement

CatShelter and get&put Cats

open class Animalclass Cat : Animal()

open class AnimalShelter { open fun getAnimalForAdoption() : Animal { return Animal() } open fun putAnimal(animal : Animal) {

}}

Base class:

class CatShelter : AnimalShelter() {

//covariant method return type override fun getAnimalForAdoption(): Cat { return Cat() }

//covariant method argument type - NOT POSSIBLE override fun putAnimal(animal: Cat) { //overrides nothing super.putAnimal(animal) }}

Derived class:

Page 12: Kotlin Advanced - language reference for Android developers

Covariance

Notes:

● Cat is a subclass of Animal

● Animal shelter puts animals and

gets them for adoption

● We want to implement

CatShelter and get&put Cats

open class Animalclass Cat : Animal()

open class AnimalShelter2<in T> { open fun getAnimalForAdoption() : Animal { return Animal() } open fun putAnimal(animal : T) { }}

Base class:

class CatShelter2 : AnimalShelter2<Cat>() {

//covariant method return type override fun getAnimalForAdoption(): Cat { return Cat() }

//covariant method argument type override fun putAnimal(animal: Cat) { super.putAnimal(animal) }}

Derived class:

Page 13: Kotlin Advanced - language reference for Android developers

Contravariance

Notes:

● Cat is a subclass of Animal

● Animal shelter puts animals and

gets them for adoption

● We want to implement

CatShelter and get&put Cats

open class Animalclass Cat : Animal()

abstract class AnimalShelter3<out T> { abstract fun getAnimalForAdoption() : T

open fun putAnimal(animal : Animal) { }}

Base class:

class CatShelter3 : AnimalShelter3<Any>() {

//contravariant method return type override fun getAnimalForAdoption(): Any { return Cat() }}

Derived class:

Page 14: Kotlin Advanced - language reference for Android developers

CovariancePreserve List<T> type safety in Java:List<String> strs = new ArrayList<String>();List<Object> objs = strs; // Java prohibits this! (Incompatible types)objs.add(1); //Cannot cast exception if compilation allowed the above line

● generic types in Java are invariantList<String> is not a subtype of List<Object>

● wildcard types in Java allow method argument type to be covariantCollection<String> is a subtype of Collection<? extends Object>(extends bound/upper-bound)

interface Collection<E> ... { void addAll(Collection<? extends E> items);}

which is why addAll() methodfrom Collection<E> is:

● doesn’t have wildcard types

● uses declaration-site variance and type projections instead

● star-projections are present

● generic constraints (upper-bounds) are possible

KOTLIN:

use-site variance

Page 15: Kotlin Advanced - language reference for Android developers

Variance comparison

JAVA

● uses wildcards to express variance● wildcards (use-site variance) are very

expressive but also hard to understand and inconvienient for programmers

KOTLIN

● developed mixed-site variance system in collaboration with Ross Tate

● combination of definition-site and use-site variance that avoids the failings of wildcards

C<? extends T>covariant instantiation of Ci.e.“C-of-some-subtype-of-T”.

Example:

fun <T : Comparable<T>> sort(list: List<T>) {}

open class AnimalShelter2< in T> {}

open class AnimalShelter2< out T> {}

Page 16: Kotlin Advanced - language reference for Android developers

Annotation

processing

Implementation state:

● old implementation: kapt worked by intercepting communication between annotation processors and javac, and added already-compiled Kotlin classes on top of the Java classes

● new implementation of KAPT: generates stubs of Kotlin classes before running javac and llows the usage of APT based libraries we already have at our current java stack

KAPT

kapt { generateStubs = true}

dependencies { kapt 'com.google.dagger:dagger-compiler:2.0.2'}

Example config:

● JSR 269 Annotation Processing available in Kotlin since M12

● Dagger 2 works :)● DBFlow works

Page 17: Kotlin Advanced - language reference for Android developers

SAM conversions

SAM call:

● function literals can be converted into implementations of Java interfaces with a single non-default method

● the parameter types of the interface method must match the parameter types of the Kotlin function

Lambda call:

mInsiderApiService.getTeamsAsync({ list -> list.forEach { item -> print(item.description) } }, { /* do nothing on error */ } )

versionTextView.setOnClickListener( View.OnClickListener { print("Message content") } )

executor.execute(Runnable { println("This runs in a thread pool") })

mInsiderApiService.getTeamsAsync( object : Callback<List<SliderItem>> {

override fun onResponse( p0: Call<List<SliderItem>>?, response: Response<List<SliderItem>>?) { /* something */ }

override fun onFailure( p0: Call<List<SliderItem>>?, p1: Throwable?) { /* something */ }})

Java interface call:

Page 18: Kotlin Advanced - language reference for Android developers

Type equality

Checking type equality

BaseClass

TallItemView

if (this instanceof TallItemView) { .... }// instanceof makes it very easy to be asymmetric

if (this.getClass() .equals(TallItemView.class) ) { .... }

Java

if (this.javaClass .isAssignableFrom(TallItemView::class.java) )

Kotlin

Referential equalityreferential equality is checked by the === operation

Structural equalitystructural equality is checked by the == operation

== is translated to: a?.equals(b) ?: (b === null)a == null is translated to: a === null

if a is not null, it calls the equals(Any?) function, otherwise b === null

Page 19: Kotlin Advanced - language reference for Android developers

Lambda vs

ClosureLAMBDA CLOSURE

● language construct● a syntax for

anonymous function● can be assigned to a

variable

● “closes over” the environment in which it was defined

● lambda which references fields external to its body

● function which is evaluated in its own environment

Page 20: Kotlin Advanced - language reference for Android developers

Reified generics

● in Java generic type parameters are not reified: they are not available at runtime

● for inline functions (inserting the function code at the address of each function call)

● safe casting of generic types● problem comes from type-erasure

class MyClass<T> { private final T o;

public MyClass() { this.o = new T(); //type parameter ‘T’ cannot be instantiated directly }}

Example (Java)

public class MyClass2<T> { @SuppressWarnings("unchecked") public T doSomething() { return (T) new MyClass(); //unchecked cast }}

class MyClass2 { inline fun <reified T> doSomething() : T { return MyClass() as T; }}class MyClass

Kotlin

Page 21: Kotlin Advanced - language reference for Android developers

Fluent interfaces

Kotlin:

https://plugins.jetbrains.com/plugin/7903

Fluent setter generator plugin for Android Studio:(JAVA)

public SampleActivity firstVariable(int firstVariable) { this.firstVariable = firstVariable; return this;}

public SampleActivity secondVariable(int secondVariable) { this.secondVariable = secondVariable; return this;}

● almost like an internal DSL● ideal for filtering, creating,

customizing etc.● used for model classes

Java:

Snakbar snack = Snackbar .make(mainView , "Sample snackbar" , Snackbar.LENGTH_LONG) .setAction( "Undo", undoClickListener) ;

Fluent interface example:

class Person(val name: String) { val parents : List<String> = arrayListOf()

constructor(name: String, parent: String) : this(name) { parents.plus(parent) }

fun parent(parent: String): Person { parents.plus(parent); return this }}

Page 22: Kotlin Advanced - language reference for Android developers

Fluent interfaces /*** Fluent sort*/fun <T : kotlin.Comparable<T>> kotlin.collections.MutableList<T>.sortList(): MutableList<T> { sort() return this}

/*** For-each fluent interface*/fun <T : kotlin.Comparable<T>> kotlin.collections.MutableList<T>.forEachList(action: (T) -> kotlin.Unit): MutableList<T> { for (elem in this) action.invoke(elem) return this}

Additional functions:

Fluent lists example:val list = listOf(1, 2, 3, 4, 5, 6, 7, 8)

list .forEach { println(it) }list forEachLoop { println(it) }

/*** In-place forEach loop (discouraged in 1.0 release)*/infix fun <T> kotlin.collections.Iterable<T> .forEachLoop(action: (T) -> kotlin.Unit): kotlin.Unit { this.forEach { action }}

val outList = list .filter { it < 100 } .filterNot { it == 1 } .toMutableList() .sortList() .forEachList { it + 1 } .filter { it % 2 == 0 } .first()

//result: 2

Page 23: Kotlin Advanced - language reference for Android developers

Infix notation

infix fun Int.minus(x: Int): Int { return this.minus(x)}

infix extension function:

val result = 1 minus 2println(3 minus 4)

type isPortfolio { println("Do something") }type isTeam { println("Do something else") }

infix fun SliderActivityType.isPortfolio( execClosure : () -> Unit ) { if (this.equals(SliderActivityType.PORTFOLIO)) execClosure.invoke()}

infix fun SliderActivityType.isTeam( execClosure : () -> Unit ) { if (this.equals(SliderActivityType.TEAM)) execClosure.invoke()}

this displayToast "This is a message"

infix fun Activity.displayToast(txt : String) { Toast.makeText(this, txt, Toast.LENGTH_SHORT).show()}

● only one method argument

● function literal can be passed outside the parentheses

Page 24: Kotlin Advanced - language reference for Android developers

Infix notation

if (this isGranted Manifest.permission.READ_CONTACTS) { //do something here}

infix fun Activity.isGranted(permissionStr : String) : Boolean { if (ContextCompat.checkSelfPermission(this, permissionStr) != PackageManager.PERMISSION_GRANTED) return false

return true}

Handle Android permissions check:

this loge "I really don't like errors"

infix fun Activity.loge(txt : String) { Log.e(this.javaClass.simpleName, txt)}

Log errors:

Page 25: Kotlin Advanced - language reference for Android developers

Static extension

methods

fun Util.isDeviceOnline(context: Context): Boolean { val connMgr = context.getSystemService(Context.CONNECTIVITY_SERVICE) as ConnectivityManager val networkInfo = connMgr.activeNetworkInfo return networkInfo != null && networkInfo.isConnected}

fun Activity.isDeviceOnline(context: Context) = { Util().isDeviceOnline(context) }fun OkHttpClient.isDeviceOnline(context: Context) = { Util().isDeviceOnline(context) }

Workaround (extension method for multiple classes):

http://stackoverflow.com/questions/28210188/static-extension-methods-in-kotlin

● In Kotlin 1.0 there is no possibility to add a static extension method

Page 26: Kotlin Advanced - language reference for Android developers

Generic types

Create generic object instance:

● in JVM generic types are lost due to type erasure

class MyClass<T> { private final T o;

public MyClass() { this.o = new T(); //type parameter ‘T’ cannot be instantiated directly }}

Page 27: Kotlin Advanced - language reference for Android developers

Generic types

val bindFunc = { baseView: FrameLayout , item: ListItem , position: Int , clickListener: View.OnClickListener -> val nameTextView = baseView.findViewById(R.id. item_simple_list_main_header) as TextView nameTextView. text = item.title baseView.setOnClickListener(clickListener)}

val adapter = SimpleItemListAdapter<ListItem , ListItemView<ListItem>>(onClickFunc , { ListItemView <ListItem> (R.layout. item_simple_list, bindFunc, baseContext , null /* attrs */ ) } );

Create generic object instance:

● in JVM generic types are lost due to type erasure

override fun onCreateItemView(parent: ViewGroup, viewType: Int): TView { val view = factory() return view as TView}

//not really convenientoverride fun onCreateItemView(parent: ViewGroup, viewType: Int, classParam : Class<T>): T { val v: T = classParam.constructors[0].newInstance(mContext, null) as T return v as T}

Page 28: Kotlin Advanced - language reference for Android developers

Sealed classes

sealed class Pet(val name: String) { class Dog(name: String): Pet(name) class Cat(name: String): Pet(name)}

Sealed class:

fun Pet.saySomething(): String { return when (this) { is Dog -> "woof" is Cat -> "meow" }}

● algebraic data type - i.e. composite data type which is formed by combining other types

● sealed classes - instead of open classes with private constructors

● “when” statement without “else” clause● “Pet” cannot have other subclasses than

“Dog” and “Cat”● since M13

// private constructor to prevent creating more subclasses outside

open class Pet private(val name: String) { class Dog(name: String): Pet(name) class Cat(name: String): Pet(name)}

Page 29: Kotlin Advanced - language reference for Android developers

Dokka

What is dokka:

KDoc documentation:● similar do Javadoc● supports stale Javadoc out of the box● markdown support included

/*** # Beacon SDK initialization** This method registers 3 beacons with IDs taken from Estimote Cloud.* Invoke this method in [onCreate].** ## Showcase demo** Steps:* * Grant application bluetooth, GPS and WiFi permissions* * Wait for about 1 minute (beacons broadcast in 0.1 ~ 2.0 sec intervals)* * Snackbar should appear on the app's main activity**/private fun initializeNearables() { ….. }

● generates documentation in html/markdown/javadoc

● maintained by Jetbrains● generated from

gradle/maven/ant● standalone executable jar

available● [ref] instead of @see ref● https://github.com/Kotlin/dokka

Page 30: Kotlin Advanced - language reference for Android developers

Dokka

Standalone jar:● java -jar dokka-fatjar.jar ./src/

buildscript { dependencies { classpath "org.jetbrains.dokka:dokka-gradle-plugin:0.9.7" }}

sourceSets { main.java.srcDirs += 'src/main/kotlin'}

dokka { outputFormat = 'html' //'markdown', 'javadoc' outputDirectory = "$buildDir/kotlindocs"}

Dokka gradle plugin:

HTML output with css styles:

Page 31: Kotlin Advanced - language reference for Android developers

J2K converter

Simple Activity conversion:

Call Java 2 Kotlin converter:

● There is no Kotlin 2 Java as for now

public class SampleActivity extends Activity { @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); }}

class SampleActivity : Activity() { override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) setContentView(R.layout.activity_main) }}

Page 32: Kotlin Advanced - language reference for Android developers

Real-world

exampleMethod count:

Library Method count

cardview-v7/23.1.0 872

play-services-maps 1369

play-services-base 700

okhttp 1463

dagger-compiler 1582

kotlin-stdlib 2411

kotlin-runtime 705

support-vector-drawable 635

butterknife 635

okio 712

(617 after proguarding)

LoC: 1857 kotlin + 503 java linesCompilation time (debug): 45.584 sec (6 sec incremental)Compilation time (release): 44.142 sec(proguard + zipaligning)

Intel® Core™ i5-3470 CPU @ 3.20GHz × 4 8 GB RAM 1333 MHz, Ubuntu 15.10, Linux kernel 4.4.2-040402-generic

Page 33: Kotlin Advanced - language reference for Android developers

Real-world

exampleMethod count:Library Method count

joda-time 1707

converter-gson 236

com.google.android.gms 1912

kotterknife 456

com.google.dagger 290

retrofit-2.0.0-beta2 593

com.android.support/design 1017

com.android.support/recyclerview-v7 1579

LoC: 1857 kotlin + 503 java linesCompilation time (debug): 47.135 sec (6 sec incremental)Compilation time (release): 53.173 sec(proguard + zipaligning)

Mac Mini late 2014, SSD 256 GB drive USB-3.02.6 GHz Intel Core i58 GB 1600 MHz DDR3, OsX El Capitan 10.11.2 (15C50)

Try it yourself - STXInsider example project:https://github.com/kosiara/stx-insider

Page 34: Kotlin Advanced - language reference for Android developers

Reflection

Calling reflection:

Text text text

dependencies { compile 'org.jetbrains.kotlin:kotlin-reflect:1.0.0'}

● Reflection is moved into separate *.jar which reduces the size of runtime library

val javaMethod = util.javaClass.methods[0]val javaConstructor = util.javaClass.constructors[0]val utilInstance = javaConstructor.newInstance()javaMethod.invoke(utilInstance)

Java reflection in Kotlin:

val classRef: KClass<Util> = Util::classval constructor = classRef.constructors.first()val method = classRef.functions.first()val util = constructor.call()method.call(util)

val aFun = classRef.functions .filter { it.name.contains("aaa") }.first()

val bFun = classRef.functions.filter { it.parameters.size == 1}.first()

aFun.call(util)bFun.call(util)

Page 35: Kotlin Advanced - language reference for Android developers

Resources

RESOURCES:

● https://en.wikipedia.org/wiki/Covariance_and_contravariance_(computer_science)● https://kotlinlang.org/docs/reference/generics.html● http://www.cs.cornell.edu/~ross/publications/mixedsite/mixedsite-tate-fool13.pdf● http://blog.jetbrains.com/kotlin/2015/06/better-annotation-processing-supporting-stubs-in-kapt/● https://yanniss.github.io/varj-ecoop12.pdf● https://schneide.wordpress.com/2015/05/11/declaration-site-and-use-site-variance-explained/● http://stackoverflow.com/questions/4231305/how-does-javas-use-site-variance-compare-to-cs-declaration-site-variance● http://www.cs.cornell.edu/~ross/publications/tamewild/● http://stackoverflow.com/questions/26507099/lambda-expressions-in-kotlin● http://gafter.blogspot.com/2006/11/reified-generics-for-java.html● http://stackoverflow.com/questions/1927789/why-should-i-care-that-java-doesnt-have-reified-generics● https://github.com/KeepSafe/dexcount-gradle-plugin● http://blog.jetbrains.com/kotlin/2015/09/kotlin-m13-is-out/● http://blog.jetbrains.com/kotlin/2011/10/dsls-in-kotlin-part-1-whats-in-the-toolbox-builders/● http://stackoverflow.com/questions/28210188/static-extension-methods-in-kotlin● http://antonioleiva.com/collection-operations-kotlin/

Page 36: Kotlin Advanced - language reference for Android developers

Thank you!

Bartosz [email protected]: @bkosarzycki

Online compiler:

http://try.kotlinlang.org/

STXInsider example project in Kotlin:https://github.com/kosiara/stx-insider