Upload
bartosz-kosarzycki
View
722
Download
6
Embed Size (px)
Citation preview
Kotlinfor Android developers
Bartosz Kosarzycki - Apr 07, 2016
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
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
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)
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
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
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
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:
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
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)
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.
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())
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
+ KOTLIN
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'
Cost
Reference application:
one blank Activity app generated with Android Studio
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);
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
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
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() }}
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}!") } }}
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
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
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
Language name origins
RUSSIAN WARSHIP
ISLAND
source:https://en.wikipedia.org/wiki/Kotlin
CITY IN POLAND
KETCHUP BRANDPROGRAMMING LANGUAGE
Live templates
exfunfun Any.f(): Unit { }
closure{ x -> x }
iterfor (i in iterable) { }
● let’s start with something simple - templates
Live templates
exvarvar Any.v: Any get() { } set(value) {
}
mainfun main(args: Array<String>) { }
innif (i != null) { }
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:
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));
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
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.
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
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:
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
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
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
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 }}
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
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
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:
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 }}
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}
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() [...]}
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
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:
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) }}
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
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
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/
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/
Thank you!
Bartosz [email protected]: @bkosarzycki
Online compiler:
http://try.kotlinlang.org/
STXInsider example project in Kotlin:https://github.com/kosiara/stx-insider