@tsmith @tsmith But I like Java! We're stuck in a Java purgatory - - PowerPoint PPT Presentation

tsmith tsmith but i like java
SMART_READER_LITE
LIVE PREVIEW

@tsmith @tsmith But I like Java! We're stuck in a Java purgatory - - PowerPoint PPT Presentation

@tsmith @tsmith But I like Java! We're stuck in a Java purgatory Error prone Code (no non-capturing anonymous inner classes) General Java language restrictions @tsmith How about Groovy? Groovy is dynamic Harder for IDEs


slide-1
SLIDE 1 @tsmith
slide-2
SLIDE 2 @tsmith
slide-3
SLIDE 3

But I like Java!

  • We're stuck in a Java purgatory
  • Error prone Code (no non-capturing anonymous inner classes)
  • General Java language restrictions
@tsmith
slide-4
SLIDE 4

How about Groovy?

  • Groovy is dynamic
  • Harder for IDEs to parse and infer
  • Won't ever be consistent with Android
@tsmith
slide-5
SLIDE 5

What is Kotlin?

  • Built by Jetbrains and Open Source
  • Official Gradle support
  • Java 6 bytecode compatible
  • Interoperable with Java
  • Small standard library (625kb)
  • Statically typed with no runtime overhead
  • Modern Language
@tsmith
slide-6
SLIDE 6

How can Kotlin help today?

  • Reduce common errors
  • Reduce boilerplate
  • Improve Android API
  • Concise and expressive for build scripts
@tsmith
slide-7
SLIDE 7

Kotlin Features

Higher-order functions, properties, mixins and delegation, extension functions, static nullability checking, automatic casts, reified generics, declaration-site variance, modules and build infrastructure, inline- functions (zero-overhead closures), operator overloading, String interpolation, pattern matching, first class IDE support with Java converter, default parameters, infix methods, and more...

@tsmith
slide-8
SLIDE 8

Nullability

"I call it my billion-dollar mistake... [which] has led to innumerable errors, vulnerabilities, and system crashes, which have probably caused a billion dollars of pain and damage in the last forty years." — Sir Charles Antony Richard Hoare

@tsmith
slide-9
SLIDE 9

Nullability

var a: String = "foo" //Reversed Type and name a = null //Compilation error

@tsmith
slide-10
SLIDE 10

Nullability

var b: String? = "bar" b = null //ok val l = b.length //Compilation error

@tsmith
slide-11
SLIDE 11

Nullability

val l = b?.length //Value null //With a default value val l = if (b != null) b.length else -1 //Value -1 //or with the Elvis operator val l = b?.length ?: -1 //Value -1

@tsmith
slide-12
SLIDE 12

Properties

class User(var name: String) { ... } fun setName(name: String): User { val user = User() // there's no 'new' keyword in Kotlin user.name = "Ty" //This is using a method, not a field return user }

@tsmith
slide-13
SLIDE 13

Properties

class User { var name: String = ... get() = name // Can declare methods inline set(value) { name = value().toLowerCase() } }

@tsmith
slide-14
SLIDE 14

Data class

data class User(name: String) { }

  • equals() and hashCode()
  • toString() of the form "User(name=John)",
  • copy()
@tsmith
slide-15
SLIDE 15

Function Literal (Lambda)

Undeclared function bodies used as an expression (ie, as data). val sum: (Int, Int) -> Int = {x, y -> x+y } val anAnswer = sum(6,7) //Variable type is inferred //Or use "it" to get the inferred parameter val double: (Int) -> Int = {it*2} val theAnswer = double(21)

@tsmith
slide-16
SLIDE 16

Higher Order Functions

A higher-order function is a function that takes functions as parameters, or returns a function

fun apply(one: Int, two: Int, func: (Int, Int) -> Int) = func(one, two) //Kotlin has great generic support to simplify this too! val multiply = apply(6, 7) {x, y -> x * y} val difgerence = apply(44, 2) {x, y -> x -y}

@tsmith
slide-17
SLIDE 17

Extension Functions

Functions added to a type without modifying the original.

fun Int.difgerenceFromTheAnswer(): Int { return 42 - this } //Or the Single Expression Function version fun Int.difgerenceFromTheAnswer(): = 42 - this //Usage val difgerence = 2.difgerenceFromTheAnswer() //40

@tsmith
slide-18
SLIDE 18

Extension Functions

Java Interoperability

//Example 1: @file:JvmName("IntUtil") fun Int.difgerenceFromTheAnswer(): = 42 - this //Example 2: @file:JvmName("Util") @file:JvmMultifileClass fun Int.difgerenceFromTheAnswer(): = 42 - this

@tsmith
slide-19
SLIDE 19

Extension Functions in Android

SharedPreferences.Editor editor; editor = getSharedPreferences(MODE_PRIVATE).edit(); editor.putString("login_token", token); editor.apply();

@tsmith
slide-20
SLIDE 20

Extension Functions in Android

Also Higher Order functions and Function Literals!

fun SharedPreferences.edit(func: SharedPreferences.Editor.() -> Unit) { val editor = SharedPreferences.Editor() editor.func() editor.apply() } getSharedPreferences(MODE_PRIVATE).edit { putString("login_token", token) }

@tsmith
slide-21
SLIDE 21

Inline Functions

//All the memory allocation fun SharedPreferences.edit(func: SharedPreferences.Editor.() -> Unit) //inlines into java byte code, matching the java signature inline fun SharedPreferences.edit(func: SharedPreferences.Editor.() -> Unit)

@tsmith
slide-22
SLIDE 22

Builders

Anko

verticalLayout { padding = dip(16) textView("Username:") { textSize = 18f }.layoutParams { verticalMargin = dip(4) } val login = editText() button("Sign up") { textSize = 20f

  • nClick { login(login.text) }

}.layoutParams { topMargin = dip(8) } }

@tsmith
slide-23
SLIDE 23

Kotlin -> Gradle

@tsmith
slide-24
SLIDE 24

Installing Kotlin

buildscript { ext.kotlin_version = '1.0.1' repositories { mavenCentral() } dependencies { classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version" } } apply plugin: 'kotlin' repositories { mavenCentral() } dependencies { compile "org.jetbrains.kotlin:kotlin-stdlib:$kotlin_version" }

@tsmith
slide-25
SLIDE 25

build.gradle.kts

buildscript { repositories { jcenter() gradleScriptKotlin() } dependencies { classpath("com.android.tools.build:gradle:2.2.0") } } apply<AppPlugin>() android { buildToolsVersion("23.0.3") compileSdkVersion(23) defaultConfigExtension { setMinSdkVersion(15) setTargetSdkVersion(23) applicationId = "com.example.kotlingradle" versionCode = 1 versionName = "1.0" } } @tsmith
slide-26
SLIDE 26

build.gradle.kts Extension Functions

fun Project.android(setup: AppExtension.() -> Unit) = the<AppExtension>().setup() fun NamedDomainObjectContainer<BuildType>.release(setup: BuildType.() -> Unit) = findByName("release").setup() fun AppExtension.defaultConfigExtension(setup: DefaultProductFlavor.() -> Unit) = defaultConfig.setup() fun AppExtension.buildTypesExtension(setup: NamedDomainObjectContainer<BuildType>.() -> Unit) = buildTypes { it.setup() } fun DefaultProductFlavor.setMinSdkVersion(value: Int) = setMinSdkVersion(value.asApiVersion()) fun DefaultProductFlavor.setTargetSdkVersion(value: Int) = setTargetSdkVersion(value.asApiVersion()) fun Int.asApiVersion(): ApiVersion = DefaultApiVersion.create(this)

@tsmith
slide-27
SLIDE 27

The case of API Keys

public class SampleApplication extends Application { private static final String CONSUMER_KEY = "PRODUCTION_KEY"; private static final String CONSUMER_SECRET = "PRODUCTION_SECRET"; @Override public void onCreate() { super.onCreate(); SessionConfig authConfig = new SessionConfig(CONSUMER_KEY, CONSUMER_SECRET); Sdk.init(authConfig); } }

@tsmith
slide-28
SLIDE 28

The case of API Keys

private static final String CONSUMER_KEY = "PRODUCTION_KEY"; private static final String CONSUMER_SECRET = "PRODUCTION_KEY"; ... final SessionConfig authConfig = new SessionConfig(CONSUMER_KEY, CONSUMER_SECRET);

@tsmith
slide-29
SLIDE 29

Enter Gradle

android { buildTypes { release { buildConfigField 'String', 'CONSUMER_KEY', "\"PRODUCTION_KEY\"" buildConfigField 'String', 'CONSUMER_SECRET', "\"PRODUCTION_SECRET\"" } debug { buildConfigField 'String', 'CONSUMER_KEY', '\"DEBUG_KEY\"' buildConfigField 'String', 'CONSUMER_SECRET', '\"DEBUG_SECRET\"' } } ...

@tsmith
slide-30
SLIDE 30

BuildConfig class

public final class BuildConfig { public static final String CONSUMER_KEY = "PRODUCTION_KEY"; ... public class AwesomeApplication extends Application { private static final String CONSUMER_KEY = BuildConfig.CONSUMER_KEY;

@tsmith
slide-31
SLIDE 31

Gradle properties

  • Gradle (Project) Properties
  • System Variables
  • Custom properties file
@tsmith
slide-32
SLIDE 32

Provided values

def getProp(String key) { if (System.properties.get(key) != null) { return System.properties.get(key) } else if (project.hasProperty(key)) { return project.property(key) } throw new GradleException("${key} is not available") }

@tsmith
slide-33
SLIDE 33

Using the provided value

//Using provided value via getProp function release { buildConfigField "String", "CONSUMER_KEY", "${getProp("INJECTED_KEY")}" ...

@tsmith
slide-34
SLIDE 34

custom.properties

INJECTED_KEY=PRODUCTION_KEY INJECTED_SECRET=PRODUCTION_SECRET ...

@tsmith
slide-35
SLIDE 35

Custom Task

task createCustomPropertiesFile << { def props = new Properties() props.put('INJECTED_KEY', getProp('INJECTED_KEY')) props.put('INJECTED_SECRET', getProp('INJECTED_SECRET')) file('custom.properties').withOutputStream { props.store(it, "") } }

@tsmith
slide-36
SLIDE 36

Custom Properties file

tasks << 'createCustomPropertiesFile' { ... }

$ ./gradlew createCustomPropertiesFile -PINJECTED_KEY=DEV_KEY

@tsmith
slide-37
SLIDE 37

Gradle plugin

Basic ingredients:

  • Task
  • Extension
  • Plugin
@tsmith
slide-38
SLIDE 38

Task

class CreatePropertiesFileTask extends DefaultTask { @TaskAction void createFile() { Properties properties = new Properties() keys?.each { properties.put(it, getProp(it)) }

  • utputFile?.withOutputStream {

properties.store(it, "") } } }

@tsmith
slide-39
SLIDE 39

Task in Java

try { Properties properties = new Properties(); if (keys != null) { for (String key : keys) { properties.put(key, getProp(key)); } } if (keyFile != null) { FileOutputStream fileOutputStream = new FileOutputStream(keyFile); properties.store(fileOutputStream, ""); } } catch (FileNotFoundException fe) { throw new GradleScriptException("Error", fe); } catch (IOException ie) { throw new GradleScriptException("Error", ie); }

@tsmith
slide-40
SLIDE 40

Task

// Properties properties = new Properties(); // for (String key : keys) { properties.put(key, getProp(key)); } // // FileOutputStream fileOutputStream = new FileOutputStream(keyFile); properties.store(fileOutputStream, ""); // // // // //

@tsmith
slide-41
SLIDE 41

Task

class CreatePropertiesFileTask extends DefaultTask { @Input List<String> keys @OutputFile File outputFile ... }

@tsmith
slide-42
SLIDE 42

Extension

class PropertyHolderExtension { List<String> keys = [] File customPropertiesFile Project project PropertyHolderExtension(Project project) { this.project = project }

@tsmith
slide-43
SLIDE 43

Extension

class KeyHolderExtension { ... void keyFile(String filename) { keyFile = new File(filename) } def getProp(String key) { if (System.properties.get(key) != null) { return System.properties.get(key) } else if (project.hasProperty(key)) { ...

@tsmith
slide-44
SLIDE 44

Plugin

class PropertyHolderPlugin implements Plugin<Project> { @Override void apply(Project project) { def propertyHolder = project.extensions.create( "propertyHolder", PropertyHolderExtension, project)

@tsmith
slide-45
SLIDE 45

Plugin

//Example 1. Without configure def propertyHolder = project.extensions.create(...) //Example 2. With configure project.configure(project) { def propertyHolder = extensions.create(...)

@tsmith
slide-46
SLIDE 46

Plugin

project.configure(project) { ... afterEvaluate { if (!propertyHolder.keys.isEmpty()) { project.tasks.create("createCustomPropertiesFile", CreatePropertiesFileTask) { it.keyFile = propertyHolder.customPropertiesFile it.keys = propertyHolder.keys } } } ...

@tsmith
slide-47
SLIDE 47

Consuming the plugin

apply 'custom-properties' propertyHolder{ keys = 'CONSUMER_KEY',... keyFile 'custom.properties' }

@tsmith
slide-48
SLIDE 48

Kotlin Version - Task

  • pen class CreatePropertiesFileTask() : DefaultTask() {

@Input var keys: List<String> = ArrayList() @OutputFile var keyFile: File? = null

@tsmith
slide-49
SLIDE 49

Kotlin Version - Task

@TaskAction fun createFile() { val properties = Properties() for (key in keys) { properties.put(key, getProp(key)) } val fileOutputStream = FileOutputStream(keyFile) properties.store(fileOutputStream, "") }

@tsmith
slide-50
SLIDE 50

Kotlin Version - Task

@TaskAction fun createFile() { val properties = Properties() keys.forEach { key -> properties.put(key, getProp(key)) } properties.store(keyFile!!.outputStream(), "") }

@tsmith
slide-51
SLIDE 51

Extension - Kotlin version

data class PropertyHolderExtension(val project: Project, var keys: List<String> = ArrayList(), var customFile: File? = null) { }

@tsmith
slide-52
SLIDE 52

Plugin - Kotlin Version

class PropertyHolderPlugin : Plugin<Project> {

  • verride fun apply(project: Project) {

project.extensions .add("kotlinHolder",PropertyHolderExtension(project)) } }

@tsmith
slide-53
SLIDE 53

Plugin - Kotlin Version

class PropertyHolderPlugin : Plugin<Project> {

  • verride fun apply(project: Project) {

project.extensions.add("kotlinHolder", PropertyHolderExtension(project)) project.afterEvaluate { project -> val extension = project.extensions.getByName("kotlinHolder") as PropertyHolderExtension } } }

@tsmith
slide-54
SLIDE 54

Sprinkle it with Extension Functions

fun Project.configure(exec: Project.() -> Unit) = exec() fun Project.extensions(exec: ExtensionContainer.() -> Unit) = extensions.exec() fun Project.propertyHolder() = extensions.getByName("kotlinHolder") as PropertyHolderExtension

@tsmith
slide-55
SLIDE 55

Kotlin Version - Plugin

//before project.extensions .add("kotlinHolder", PropertyHolderExtension(project)) //after project.configure { extensions { add("kotlinHolder", PropertyHolderExtension(project)) } }

@tsmith
slide-56
SLIDE 56

Kotlin Version - Plugin

project.configure { extensions { add("kotlinHolder", PropertyHolderExtension(project)) } afterEvaluate { project -> if (!propertyHolder().keys.isEmpty()) { tasks.create("createPropertiesFile", CreatePropertiesFileTask::class.java) { task -> task.keys = propertyHolder().keys task.keyFile = propertyHolder().customFile!! } } } }

@tsmith
slide-57
SLIDE 57

Resources

  • Kotlin docs
  • Kotlin Koans
  • Anko
  • Gradle Script Kotlin
  • Advancing Android Development with the Kotlin Language
  • Gradle Kotlin Demo
@tsmith
slide-58
SLIDE 58

Thanks!

Ty Smith

@tsmith
slide-59
SLIDE 59 @tsmith