Successfully reported this slideshow.
We use your LinkedIn profile and activity data to personalize ads and to show you more relevant ads. You can change your ad preferences anytime.

ADG Poznań - Kotlin for Android developers

1,428 views

Published on

Android Developer Group Poznań - Kotlin for Android developers

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

Kotlin - one of the popular programming languages built on top of Java that runs on JVM. Thanks to JetBrains support and excellent IDE integration, it’s an ideal choice for Android development. 100% Java compatibility, interoperability and no runtime overhead is just the beginning of a long list of strengths. Kotlin is supposed to be a subset of SCALA, has clear benefits for developers on one hand and keeps short compile times on the other.

As a mobile team we got interested in Kotlin a few months before its final release which gave us time to test it thoroughly before production use. The language has some clear advantages for an Android programmer - it enables migration from Java projects that have been under development for some time already. Java&Kotlin coexistence simplifies Kotlin introduction as only new functionality is written in JetBrain’s new language leaving all the legacy code untouched.
Transitioning gives the developer an opportunity to use lambdas, new syntax for data objects, extension functions to easily expand Android SDK’s classes functionality and infix notation to write DSL-like structures. Almost all the libraries you use today will work with Kotlin thanks to 100% Java compatibility. The same is true for Android SDK classes - all of them will seamlessly work with the new programming language. Kotlin gives you more choice when it comes to reflection, creating documentation and being null-pointer safe. Android works great with it out of the box so you won’t need to change your development habits.

Our production project in Kotlin turned out to be a success after 4 months of development. We had 0 bugs related to Kotlin as a programming language. Our code footprint is almost 30% smaller thanks to JetBrain’s, we benefit from nullpointer safety, closures, translated enums, data objects and use infix notation for logging and displaying Snackbars.

===========

In this presentation you'll find basic use cases, syntax, structures and patterns. Later on Kotlin is presented in Android context. Simple project structure, imports and Kotlin usage with Android SDK is explained. In the end cost of Kotlin compilation is presented and the language is compared to SCALA and SWIFT.

We look at the positive impact new syntax can have on boilerplate removal and readability improvement.
Kotlin really shines in Android development when one looks at “Enum translation”, “Extension functions”, “SAM conversions”, “Infix notation”, “Closures” and “Fluent interfaces” applied to lists. The talk, however, compares language-specifics of Java & Kotlin in terms of “Type Variance”, “Generics” and “IDE tools” as well.

Published in: Software
  • Be the first to comment

ADG Poznań - Kotlin for Android developers

  1. 1. Kotlin for Android developers Bartosz Kosarzycki - Apr 07, 2016
  2. 2. 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
  3. 3. 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
  4. 4. 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)
  5. 5. 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
  6. 6. 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
  7. 7. Nullpointer safety var output : String? output = null println(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 = null println(output?.length) val len = output?.length ?: -1 //elvis operator println(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.Unit If a function does not return any useful value, its return type is Unit
  8. 8. Advantages val 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:
  9. 9. 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
  10. 10. 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)
  11. 11. Delegation pattern 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.
  12. 12. 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())
  13. 13. Utility classes UTILS: StringUtil ActivityUtil ListUtil JAVA utility class public class StringUtils { public static String encodeString(String str) { return str.replaceAll(" ", "_"); } } KOTLIN utility class fun 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
  14. 14. + KOTLIN
  15. 15. 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'
  16. 16. Cost Reference application: one blank Activity app generated with Android Studio
  17. 17. Higher-order functions Introduction //extension function fun Date.isTuesday() = day == 2 //function expression as contant val addition = { x: Int, y:Int -> x + y } //higher order function fun higherOrder(x : Int, y: Int, func : (x : Int, y : Int) -> Int ) : Int { return func.invoke(x, y); } Higher-order extension function fun 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);
  18. 18. Android Compactness mDescriptionTextView.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() } } ● simplify development ● remove boilerplate ● improve multi-threading Auto getters&setters
  19. 19. 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
  20. 20. Dagger 2 & KOTLIN @Module class 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 KOTLIN since 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() } }
  21. 21. Kotlin Anko override 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}!") } } }
  22. 22. 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
  23. 23. 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
  24. 24. 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 SDKMAN or homebrew Linux FreeBSD Cygwin SDKMAN
  25. 25. Language name origins RUSSIAN WARSHIP ISLAND source: https://en.wikipedia.org/wiki/Kotlin CITY IN POLAND KETCHUP BRANDPROGRAMMING LANGUAGE
  26. 26. Live templates exfun fun Any.f(): Unit { } closure { x -> x } iter for (i in iterable) { } ● let’s start with something simple - templates
  27. 27. Live templates exvar var Any.v: Any get() { } set(value) { } main fun main(args: Array<String>) { } inn if (i != null) { }
  28. 28. Enum translation Kotlin enum class SliderActivityType private constructor(val title: Int) { PORTFOLIO(R.string.portfolio), TEAM(R.string.team) } Java public 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:
  29. 29. Calling extension functions Get app-version ext. function: @file:JvmName("ActivityUtil") //@file:JvmMultifileClass package 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));
  30. 30. 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
  31. 31. 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.
  32. 32. 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
  33. 33. 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:
  34. 34. 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 equality referential equality is checked by the === operation Structural equality structural 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
  35. 35. Lambda vs Closure LAMBDA 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
  36. 36. 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
  37. 37. 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 } }
  38. 38. 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
  39. 39. Infix notation infix fun Int.minus(x: Int): Int { return this.minus(x) } infix extension function: val result = 1 minus 2 println(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
  40. 40. 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:
  41. 41. 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 } }
  42. 42. 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 convenient override 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 }
  43. 43. 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() [...] }
  44. 44. 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
  45. 45. 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:
  46. 46. 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) } }
  47. 47. Real-world example Method 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 lines Compilation 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
  48. 48. Real-world example Method 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 lines Compilation 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.0 2.6 GHz Intel Core i5 8 GB 1600 MHz DDR3, OsX El Capitan 10.11.2 (15C50) Try it yourself - STXInsider example project: https://github.com/kosiara/stx-insider
  49. 49. 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/
  50. 50. 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/
  51. 51. Thankyou! Bartosz Kosarzycki bartosz.kosarzycki@stxnext.pl Twitter: @bkosarzycki Online compiler: http://try.kotlinlang.org/ STXInsider example project in Kotlin: https://github.com/kosiara/stx-insider

×