51
Kotlin for Android developers Bartosz Kosarzycki - Apr 07, 2016

ADG Poznań - Kotlin for Android developers

Embed Size (px)

Citation preview

Page 1: ADG Poznań - Kotlin for Android developers

Kotlinfor Android developers

Bartosz Kosarzycki - Apr 07, 2016

Page 2: ADG Poznań - Kotlin for Android developers

Kotlin for Android developers❏ What is Kotlin?❏ Why Kotlin?❏ JAVA 8 JACK compiler❏ Advantages❏ Nullpointer safety❏ Advantages❏ Data objects❏ Traits**❏ Delegation pattern❏ Properties❏ Utility classes❏ Project structure in Android❏ Cost of Kotlin in Android❏ Higher-order functions❏ Lambda & getters and setters❏ Kotterknife❏ Dagger 2❏ Kotlin Anko❏ Sample Activity in Kotlin❏ SWIFT & KOTLIN comparison❏ Command line compiler

AGENDA

❏ Language name origins❏ Live templates❏ Enum translation❏ Calling extension functions from Kotlin/Java❏ Constructors with backing fields❏ Warnings❏ Annotation processing - KAPT❏ SAM conversions❏ Type equality❏ Lambda vs Closure❏ Reified generics❏ Fluent interfaces❏ Infix notation❏ Static extension methods in Kotlin❏ Generic types❏ Dokka - documentation in Kotlin❏ J2K converter❏ Real-world example

Page 3: ADG Poznań - Kotlin for Android developers

What is Kotlin?

KOTLIN is:● safe● versatile● interoparable● IDE support● fast

JAVA SCALA

KOTLIN+ fast compilation+ simplicity+ swift’s syntax is similar

Online compiler:http://try.kotlinlang.org/

SWIFT

Required knowledge:● basic Android development skills● functional programming● familiarity with JDK 6,7,8● Scala is a plus

Page 4: ADG Poznań - Kotlin for Android developers

Why KOTLIN?

● no javax.time from JDK8 ● no try-with resources● no lambdas!● no new java stream api ● no way to add methods to platform data

types (e.g. View) List<string> names = students.stream() .map(Student::getName) .filter(name->name.startsWith("B")) .collect(Collectors.toList());

ZoneId zone = ZoneId.systemDefault();Clock clock = Clock.system(zone);LocalDate date = LocalDate.now();LocalTime time = LocalTime.now();time = time.plus(Period.ofDays(12));

javax.time

Java Stream API

static String readFirstLineFromFile(String path) throws IOException { try (BufferedReader br = new BufferedReader(new FileReader(path))) { return br.readLine(); }}

try-with resources

** JACK compiler is in beta (Android N)

Page 5: ADG Poznań - Kotlin for Android developers

JAVA 8 JACK

● Default and static interface methods

● Lambda expressions● Repeatable annotations

** JACK compiler is in beta (Android N)

@Schedule(dayOfMonth= "last")@Schedule(dayOfWeek= "Fri", hour="23")public void doPeriodicCleanup () { ... }

Repeatable annotations

printPersons( roster, (Person p) -> p.getGender() == Person.Sex.MALE && p.getAge() >= 18 && p.getAge() <= 25);

Lambda expressions

public interface Java8DefaultInterface { default void closeSession() { Log.i("TAG", "Session closed"); }}

Default interface methods

Page 6: ADG Poznań - Kotlin for Android developers

Advantages

fun finish(obj: Any) { if (obj is Activity) obj.finish()}

Auto-casting:

Named args in func calls:fun circle(x: Int, y: Int, rad: Int, stroke: Int) {…}

circle(15, 40, rad = 20, stroke = 1);

Built-in lambdas:val numbers: IntArray = intArrayOf(11, 20, 31, 40, 51)val predicate: (Int) -> Boolean = { it -> it % 2 == 1 }

val list1 = numbers.filter { it % 2 == 1 }val list2 = numbers.filter(predicate)

println("Lists identical: " + list1.equals(list2));

> Lists identical: true

Compactness:* no new statement:val a = B();

* optional brackets, return statement and one-line function declarations:class A { var field1: String = "No "; fun printNews() = field1 + " news for you" ;}println(A().printNews())

● all of these are much-needed in Android development

Page 7: ADG Poznań - Kotlin for Android developers

Nullpointer

safetyvar output : String? output = nullprintln(output!!.length)

Exception in thread "main" kotlin.KotlinNullPointerException at Simplest_versionKt.main(Simplest version.kt:11)

Java-like !! Operator: (for NPE lovers) - Optional.get() equivalent

var output : String? output = nullprintln(output?.length)

val len = output?.length ?: -1 //elvis operatorprintln(len)

> null> -1

?. Safe calls: (for if not null -> call function; return null otherwise)

Kotlin type aliases - planned in roadmap(not yet released - as of Feb 2016)

Java JDK 10 will push Optional onto the default stack ~ 2018

Optional<> pattern no longer needed!

kotlin.UnitIf a function does not return any useful value, its return type is Unit

Page 8: ADG Poznań - Kotlin for Android developers

Advantagesval arr = arrayOf(D("1A", "1B"), D( "2A", "2B"), D("3A", "3B"));for ((first, second) in arr ) println("a: $first, b: $second")

class D { public var nameA: String = "" public var nameB: String = ""

constructor(nameA: String , nameB: String) { this.nameA = nameA this.nameB = nameB }

operator fun component1() : String { return nameA } operator fun component2() : String { return nameB }}

(a, b) Destructuring Declaration

Singleton:object SampleSingleton { var baseUrl: String = "https://aaa.bbb" fun printUrl() = println(baseUrl)}

SampleSingleton.printUrl()SampleSingleton.baseUrl = "https://ccc.ddd"SampleSingleton.printUrl()

> https://aaa.bbb> https://ccc.ddd

● Singletons and destructuring declarations are built-in:

Page 9: ADG Poznań - Kotlin for Android developers

Data objects

data class Point(val x: Double = 0.0, val y: Double = 0.0, var descr: String?)

val point1 = Point( x = 1.0, y = 2.0, descr = "no description");val point2 = Point( descr = "no description", y = 2.0, x = 1.0);println(point1.equals(point2))println(point1.hashCode().equals(point2.hashCode()) )println(point1.toString().equals(point2.toString()) )println(point1.toString())

Data object:

hashCode()toString()equals()+ properties

Automatically generated:● removes most of the boilerplate code

Page 10: ADG Poznań - Kotlin for Android developers

Traits**

TRAITS - java-like interfaces with default implementation

class ExampleActivity : AppCompatActivity(), ActivitySessionHandling { override fun onDestroy() { super.onDestroy() closeSession() }}

open interface ActivitySessionHandling { fun closeSession() = println("Session closed")}

JAVA JDK 8 - extension methods - default interface implementation

public class ItemListActivity extends AppCompatActivity implements Java8DefaultInterface { @Override protected void onDestroy() { super.onDestroy(); closeSession(); }}

public interface Java8DefaultInterface { default void closeSession() { Log.i("TAG", "Session closed"); }}

**JAVA 8 (JACK - Android)

Page 11: ADG Poznań - Kotlin for Android developers

Delegationpattern

Built-in delegate pattern:class Derived(b: Base) : Base by b

class BaseImpl(val x: Int) : Base { override fun print() { println(x) }}

interface Base { fun print()}

val b = BaseImpl(10)Derived(b).print()

> 10

Java equivalent:class Derived { Base base; //helper object

public Derived(Base b) { this.base = b; }

void print(){ base.print(); }}

class BaseImpl implements Base { int val;

public BaseImpl(int v) { this.val = v; }

public void print() { System.out.println(val); }}

interface Base { void print();}

BaseImpl base = new BaseImpl(10);new Derived(base).print();

In Delagation Pattern an object instead of performing one of its stated tasks, delegates that task to an associated helper object. There is an Inversion of Responsibility.

Page 12: ADG Poznań - Kotlin for Android developers

Properties

Properties & read-only properties:public class Address(addr : String) { public var name: String = "" public val address: String = addr //read-only}

val address = Address(addr = "Low street 123")address.name = "Mickey mouse"println(address.address)println(address.name)

> Low street 123> Mickey mouse

address.address = "Another street 123" //Error: val cannot be reassigned

Getters & setters:public class Address() { var address: String get() = "Lorem ipsum" set(value) { println(value) }}

public class Address() { var address: String = "" get //default getter private set //default private setter}

Companion objects:class CarAssemblyFactory { companion object Factory { fun createCar(): String = String().plus("This is a car") }}

println(CarAssemblyFactory.createCar())

Page 13: ADG Poznań - Kotlin for Android developers

Utility classes

UTILS:StringUtilActivityUtilListUtil

JAVA utility classpublic class StringUtils { public static String encodeString(String str) { return str.replaceAll(" ", "_"); }}

KOTLIN utility classfun String.encodeSpaces():String = this.replace(" ", "_")

println("Neque porro quisquam".encodeSpaces())

Separate packages:package main.kotlin.utils

fun String.encodeSpaces(): String = this.replace(" ", "_")

import main.kotlin.utils.encodeSpaces

println("Neque porro quisquam".encodeSpaces())

● no utils hell● extend final classes● classes in Kotlin are

final by default

@JvmName("DateUtil")fun Date.isTuesday() = day == 2 //in fact it's: //Date.getDay() == 2

Page 14: ADG Poznań - Kotlin for Android developers

+ KOTLIN

Page 15: ADG Poznań - Kotlin for Android developers

Project

structure

Android kotlin project structure: Kotlin & Java intertwined:

Gradle dependencies:build.gradle:

buildscript { dependencies { classpath 'com.android.tools.build:gradle:2.0.0-beta3' classpath 'org.jetbrains.kotlin:kotlin-gradle-plugin:1.0.0-rc-1036' }}

app.gradle:

apply plugin: 'kotlin-android'

Page 16: ADG Poznań - Kotlin for Android developers

Cost

Reference application:

one blank Activity app generated with Android Studio

Page 17: ADG Poznań - Kotlin for Android developers

Higher-order

functionsIntroduction//extension functionfun Date.isTuesday() = day == 2

//function expression as contantval addition = { x: Int, y:Int -> x + y }

//higher order functionfun higherOrder(x : Int, y: Int, func : (x : Int, y : Int) -> Int ) : Int { return func.invoke(x, y); }

Higher-order extension functionfun Int.addCustomFunc(arg: Int, func : (x : Int, y : Int) -> Int ) : Int { return func.invoke(this, arg); }val addition = { x: Int, y:Int -> x + y }val result = 1.addCustomFunc(5, addition);

Page 18: ADG Poznań - Kotlin for Android developers

Android

CompactnessmDescriptionTextView.setOnClickListener({ activityPrivateMethod() })mDescriptionTextView.setOnClickListener({ mDescriptionTextView.text = "Current time: " + DateTime.now() })

Lambda expression to the rescue

private fun bind(item: Team) { mHeaderTextView.text = item.header mDescriptionTextView.text = item.description}

mSampleEditText.addTextChangedListener{ onTextChanged { text:CharSequence, a:Int, b:Int, c:Int -> Toast.makeText(applicationContext, text, LENGTH_SHORT).show() } }

Lambda expression to the rescue

● simplify development● remove boilerplate● improve multi-threading

Auto getters&setters

Page 19: ADG Poznań - Kotlin for Android developers

Kotterknife

val mHeaderTextView: TextView by bindView(R.id.activity_team_details_team_header)val mDescriptionTextView: TextView by bindView(R.id.activity_team_details_team_description)

val textViews: List<TextView> by bindViews(R.id.activity_team_details_team_header, R.id.activity_team_details_team_description)

// List binding with optional items being omitted.val nameViews: List<TextView> by bindOptionalViews(R.id.first_name, R.id.middle_name, R.id.last_name)

● bindView() instead of @Bind annotation

● developed by Jake Wharton● still not pushed to maven

central

Page 20: ADG Poznań - Kotlin for Android developers

Dagger 2

& KOTLIN

@Moduleclass AndroidModule(private val application: Application) {

@Provides @Singleton fun provideLocationManager(): LocationManager { return application .getSystemService(Context.LOCATION_SERVICE) as LocationManager }

@Provides @Singleton @Named("something") fun provideSomething(): String { return "something" }}

class MainActivity : AppCompatActivity() { @Inject lateinit var locationManager: LocationManager

@field:[Inject Named("something")] lateinit var something: String

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

● compatible with KOTLINsince M13

● introduction of lateinit property

Late-initialized property: e.g. for unit tests

public class MyTest { lateinit var subject: TestSubject

@SetUp fun setup() { subject = TestSubject() }

@Test fun test() { subject.method() }}

Page 21: ADG Poznań - Kotlin for Android developers

Kotlin Ankooverride fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState)

verticalLayout { padding = dip(30) editText { hint = "Name" textSize = 24f } editText { hint = "Password" textSize = 24f } button("Login") { textSize = 26f } }}

● from Jetbrains● create layouts from code

dependencies { compile 'org.jetbrains.anko:anko-sdk15:0.8' compile 'org.jetbrains.anko:anko-support-v4:0.8' compile 'org.jetbrains.anko:anko-appcompat-v7:0.8' }

verticalLayout { val name = editText() button("Say Hello") { onClick { toast("Hello, ${name.text}!") } }}

Page 22: ADG Poznań - Kotlin for Android developers

Sample Activity

class TeamDetailsActivity : AppCompatActivity() {

val mHeaderTextView: TextView by bindView(R.id.activity_team_details_team_header) var mTeam: Team? = null

override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) setContentView(R.layout.activity_team_details) supportActionBar!!.title = "Team description" supportActionBar!!.setDisplayHomeAsUpEnabled(true)

mTeam = Gson().fromJson<Team>(intent.getStringExtra("item"), Team::class.java) bind(mTeam!!) }

private fun bind(item: Team) { mHeaderTextView.text = item.header }

override fun onOptionsItemSelected(item: MenuItem): Boolean { if (item.itemId == android.R.id.home) finish()

return super.onOptionsItemSelected(item) }}

● 100% Java compatibility● kotterknife● Android SDK usage in Kotlin is simple

Page 23: ADG Poznań - Kotlin for Android developers

SWIFT

var myVariable = 42 //Variable

val explicitDouble: Double = 70.0 //Explicit Type Constant

for (i in 1..5) { print(i) } //Inclusive Range Operator

val a = "A"; val b = "B"; val str = "I have ${a + b} "; //String interpolation

var shoppingList = arrayOf("catfish", "water") //Array Creation

var hashMap = hashMapOf("Malcolm" to "Captain"); //Maps

val emptyArray = arrayOf<String>() //Empty Typed Array

interface Nameable { fun name(): String } //Interface

val movie = obj as Movie //Downcasting

fun Double.km() : Double = this * 1000; //Extension function

KOTLIN SWIFT

var myVariable = 42 //Variable

let explicitDouble: Double = 70 //Explicit Type Constant

for i in 1...5 { println( i) } //Inclusive Range Operator

let a = "A"; let b = "B"; let str = "I have \(a + b) " //String interpolation

var shoppingList = [ "catfish" , "water"] //Array Creation

var occupations = [ "Malcolm" : "Captain"] //Maps

let emptyArray = String[]() //Empty Typed Array

protocol Nameable { func name() -> String } //Protocol (Interface)

let movie = object as Movie //Downcasting

extension Double { var km: Double { return self * 1_000.0 } } //Extension function

● Kotlin’s syntax is similar Swift

Page 24: ADG Poznań - Kotlin for Android developers

Command line

compiler

$ curl -s get.sdkman.io | bash

$ source "$HOME/.sdkman/bin/sdkman-init.sh"

$ sdk install kotlin

Do you want kotlin 1.0.0-rc-1036 to be set as default? (Y/n): Y

Setting kotlin 1.0.0-rc-1036 as default.

Installation:

● Let us compare the resulting bytecode of kotlin and java compilation

OsX

SDKMANor homebrew

LinuxFreeBSDCygwin

SDKMAN

Page 25: ADG Poznań - Kotlin for Android developers

Language name origins

RUSSIAN WARSHIP

ISLAND

source:https://en.wikipedia.org/wiki/Kotlin

CITY IN POLAND

KETCHUP BRANDPROGRAMMING LANGUAGE

Page 26: ADG Poznań - Kotlin for Android developers

Live templates

exfunfun Any.f(): Unit { }

closure{ x -> x }

iterfor (i in iterable) { }

● let’s start with something simple - templates

Page 27: ADG Poznań - Kotlin for Android developers

Live templates

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

}

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

innif (i != null) { }

Page 28: ADG Poznań - Kotlin 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 29: ADG Poznań - Kotlin 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 30: ADG Poznań - Kotlin 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 31: ADG Poznań - Kotlin 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 32: ADG Poznań - Kotlin 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 33: ADG Poznań - Kotlin 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 34: ADG Poznań - Kotlin 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 35: ADG Poznań - Kotlin 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 36: ADG Poznań - Kotlin 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 37: ADG Poznań - Kotlin 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 38: ADG Poznań - Kotlin 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 39: ADG Poznań - Kotlin 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 40: ADG Poznań - Kotlin 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 41: ADG Poznań - Kotlin 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 42: ADG Poznań - Kotlin 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 43: ADG Poznań - Kotlin for Android developers

Click annotations

KClick:

init { R.id.sendButton bind KClick(this, { v: View -> onSendButtonClick(v) }) }fun onSendButtonClick(v: View) { [...]}

● custom function written in Kotlin

● uses infix notation and extension functions

Bind button click using DSL:

/** init {* R.id.loginButton bind KClick(this, { v: View -> onLoginButtonClick(v) })* R.id.logoutButton bind KClick(this, { v: View -> onLogoutButtonClick(v) })* }*/infix fun Int.bind(kClick: KClick): kotlin.Unit { var kViewEntries = Util.kViewsMap[(kClick.activity.javaClass.name)] if (kViewEntries == null) kViewEntries = mutableListOf() kViewEntries.add(KViewEntry(this, { v: View -> kClick.action.invoke(v) }) ) Util.kViewsMap.put(kClick.activity.javaClass.name, kViewEntries)}data class KClick(val activity: Activity, val action: (View) -> kotlin.Unit)data class KViewEntry(val id: Int, val action: (View) -> kotlin.Unit)fun Activity.bindKViews() { for (mutEntry in Util.kViewsMap) if (mutEntry.key == this.javaClass.name) { val kViewEntries = mutEntry.value; for (kViewEntry in kViewEntries) findViewById(kViewEntry.id).setOnClickListener { v: View -> kViewEntry.action.invoke(v) }

Util.kViewsMap.remove(mutEntry.key) }}

override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) bindKViews() [...]}

Page 44: ADG Poznań - Kotlin 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 45: ADG Poznań - Kotlin 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 46: ADG Poznań - Kotlin 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 47: ADG Poznań - Kotlin 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 48: ADG Poznań - Kotlin 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 49: ADG Poznań - Kotlin for Android developers

Resources

RESOURCES:

● http://kotlinlang.org/

● Jake Wharton - Using Project Kotlin for Android - https://t.co/9t8qsBGPlo

● Mike Gouline - Kotlin the Swift of Android - - https://blog.gouline.net/2014/08/31/kotlin-the-swift-of-android/

● Paweł Gajda - Kotlin Android - http://slides.com/pawegio/kotlin-android, https://github.com/pawegio/KAndroid

● Jetbrains Kotlin Anko - https://github.com/Kotlin/anko

● Amit Phaltankar Java 8 streams - https://dzone.com/articles/understanding-java-8-streams-1

● Salomon Brys - https://github.com/SalomonBrys

● Gautier Mechling - http://nilhcem.github.io/swift-is-like-kotlin/

Page 50: ADG Poznań - Kotlin 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 51: ADG Poznań - Kotlin 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