Upgrade to Pro — share decks privately, control downloads, hide ads and more …

Kotlin for Grooviers

Alonso Torres
February 12, 2019

Kotlin for Grooviers

A side-by-side comparison between Groovy and Kotlin programming languages.

Alonso Torres

February 12, 2019
Tweet

Other Decks in Programming

Transcript

  1. Groovy (2003) • A better Java! • Dynamic typing •

    Functional style • Scripting • DSL capabilities • Metaprogramming
  2. Kotlin (2012) • A better Java! • Static typing •

    Functional style • Scripting • DSL capabilities
  3. Groovy (2003) • A better Java! • Dynamic typing •

    Functional style • Scripting • DSL capabilities • Metaprogramming
  4. - Slower compilation times - Verbosity - No runtime metaprogramming

    - Harder reflection + Move runtime errors to compile time + Better tooling support + Faster runtime Static types languages
  5. Design Goals • Full Java interoperability • Compile as fast

    as Java • Safer than Java • More concise than Java • Way simpler than Scala
  6. Features • Higher-order functions • Properties • Mixins and First-class

    delegation • Extension functions • Static nullability checking • Automatic casts • Reified generics • Declaration-site variance • Modules and Build infrastructure • Inline-functions • Pattern matching • Type inference • Multiplatform (Android, JS, Native) • Coroutines & suspending functions • Scripting • Infix functions • String templates • Range expressions • Operator overloading • First-class tooling
  7. Features • Higher-order functions • Properties • Mixins and First-class

    delegation • Extension functions • Static nullability checking • Automatic casts • Reified generics • Declaration-site variance • Modules and Build infrastructure • Inline-functions • Pattern matching • Type inference • Multiplatform (Android, JS, Native) • Coroutines & suspending functions • Scripting • Infix functions • String templates • Range expressions • Operator overloading • First-class tooling
  8. Integer factorial(Integer num) { def result = 1 (1..num).forEach {

    result = result * it } return result } fun factorial(num: Int): Int { var result = 1 (1..num).forEach { result = result * it } return result }
  9. • String templates • Multiline strings • Ranges • Closures

    syntax • Closure implicit parameter (it) • Closure call on last parameter • Null safe navigation • Elvis operator • Builder concept • in, as operators
  10. def foo = "bar" final def secret = 42 var

    foo = "bar" val secret = 42 String foo = "bar" Float PI var foo: String = "bar" var PI: Float Variable definition
  11. def foo = "bar" foo = 10 foo.toUpperCase() var foo

    = "bar" foo = 10 Compilation error: foo of type String Compilation error: foo of type Integer Type inference
  12. var foo = "bar" foo = 10 foo.toUpperCase() Compilation error:

    Any doesn’t have method def foo = "bar" foo = 10 foo.toUpperCase() Type inference
  13. if (x instanceof String) { println(((String)x).toUpperCase()) } else if (x

    instanceof Integer) { println(((Integer)x) + 2) } if (x is String) { println(x.toUpperCase()) } else if (x is Integer) { println(x + 2) } Smart casting
  14. if (x > 0) { println("Greater than zero") } if

    (x > 0) { println("Greater than zero") } Basic statements for (num in (1 .. 20)) { println("Value: $i") } for (num in (1 .. 20)) { println("Value: $i") }
  15. when (value) { 42 -> println("Int") is String -> println("String")

    in 0..20 -> println("In range") else -> println("Other") } switch (val) { case 42: println "Int"; break case String: println "String"; break case 0..20: println "In range"; break case { "$it".length < 50 }: println "Closure"; break default: println "Other"; break } When statement
  16. if (....) { … } else { … } try

    { … } catch { … } switch (...) { … } for (...) { … } 1 + 1 doStuff() obj.test() Expressions Statements Statements as expressions
  17. if (....) { … } else { … } try

    { … } catch { … } switch (...) { … } for (...) { … } x = 1 + 1 if (doStuff()) { … } println(obj.test()) Expressions Statements Statements as expressions
  18. x = if (....) { … } else { …

    } x = try { … } catch { … } x = when (...) { … } for (...) { … } x = 1 + 1 if (doStuff()) { … } println(obj.test()) Expressions Statements as expressions Statements as expressions
  19. val message = if (success) "OK" else "Error" val message

    = when (success) { true -> "OK" false -> "Error" } val num = try { parseInt(args[0]) } catch (e: Exception) { -1 } Statements as expressions
  20. Integer x, y x = (y = 10) var x:

    Int var y: Int x = (y = 10) Statements as expressions
  21. Integer sum(Integer a, Integer b) { return a + b

    } fun sum (a: Int, b: Int): Int { return a + b } Basic functions definitions
  22. Integer sum(Integer a, Integer b) { a + b }

    fun sum (a: Int, b: Int) = a + b Single expression functions
  23. String message (Boolean success) { switch (success) { case true:

    return "OK" case false: return "Error" } } fun message (success: Boolean) = when (success) { true -> "OK" false -> "Error" } Single expression functions
  24. Integer sum(Integer... nums) { nums.sum() } sum(1, 2, 3, 4)

    fun sum (vararg nums: Int) = nums.sum() sum(1, 2, 3, 4) Variable number of arguments (Varargs)
  25. String greet( String message = "Hi", String name = "you")

    { "$message $name" } greet("Hello") greet("Hello", "GUG") fun greet( message: String = "Hi", name: String = "you" ): String = "$message $name" greet("Hello") greet("Hello", "GUG") Default parameters
  26. String greet(Map options) { def msg = options.message ?: "Hi"

    def name = options.name ?: "you" return "$msg $name" } greet message: "Hello" greet name: "folks", message: "Hey" fun greet( message: String = "Hi", name: String = "you" ): String = "$message $name" greet(message = "Hello") greet(name="folks", message="Hey") Named parameters
  27. class Main { static void main(args: String[]) { println("Hello ${args[0]}")

    } } fun main(args: Array<String>) { println("Hello ${args[0]}") } Function usage and imports
  28. package test fun doStuff = println("OK!") val SO_SO_PI = 3.15

    import test.doStuff import test.SO_SO_PI fun main() { doStuff() println(SO_SO_PI) } Function usage and imports
  29. package test object Helpers { fun doStuff() = println("OK") }

    import test.Helpers fun main() { Helpers.doStuff() } Function usage and imports
  30. package test object Helpers { fun doStuff() = println("OK") }

    import test.Helpers.doStuff fun main() { doStuff() } Function usage and imports
  31. def list = [ 1, 2, 3 ] def map

    = [ "uno": 1, "dos": 2 ] val list = listOf(1, 2, 3) val map = mapOf( "uno" to 1, "dos" to 2 ) list.add(100) map["catorce"] = 14 list.add(100) map["catorce"] = 14 Collections
  32. def list = [ 1, 2, 3 ] def map

    = [ "uno": 1, "dos": 2 ] val list = mutableListOf(1, 2, 3) val map = mutableMapOf( "uno" to 1, "dos" to 2 ) list.add(100) map["catorce"] = 14 list.add(100) map["catorce"] = 14 Mutable collections
  33. val arr: Array<Int> = Array(3, { it }) arr[0] =

    10 arr[0] = 10 def arr = [ 1, 2, 3 ] as Int[] val arr = arrayOf(1, 2, 3) Arrays
  34. list.collect { it * 2 } list.findAllBy { it %

    2 == 0 } list.inject(0) { acc, v -> acc + v } list.map { it * 2 } list.filter { it % 2 == 0 } list.reduce { acc, v -> acc + v } Collections higher order functions
  35. fun mainLoop(): Nothing { while(true) { doThings() } } fun

    main(args: Array<String>) { mainLoop() println("End") } Nothing type
  36. fun main(args: Array<String>) { val result = processParameters(args) } fun

    processParameters(args: Array<String>): String = TODO("Process params list") Placeholder for TODO functions
  37. .. a null reference can be used to indicate that

    the data is missing or not known at this time. This led me to suggest that the null value is a member of every type, and a null check is required on every use of that reference variable, and it may be perhaps a billion dollar mistake. - Tony Hoare
  38. @Inject MyUserService service User user = service.retrieveUser() user.name.toUpperCase() User user

    = service?.retrieveUser() user?.name?.toUpperCase() Handling null references
  39. -MAX_INT, … -5, -4, -3, -2, -1, 0, 1, 2,

    3, 4, 5, 6, 7, 8, ... MAX_INT, null java.lang.Integer -MAX_INT, … -5, -4, -3, -2, -1, 0, 1, 2, 3, 4, 5, 6, 7, 8, ... MAX_I NT, null kotlin.Int? -MAX_INT, … -5, -4, -3, -2, -1, 0, 1, 2, 3, 4, 5, 6, 7, 8, ... MAX_INT kotlin.Int
  40. val foo: String = null var foo: String? = null

    foo = "bar" val myList: List<Int>? = null val myMap: Map<String, Int?> = mapOf("test" to null) Compilation error Null types
  41. @Inject MyUserService service User user = service.retrieveUser() Only safe (?.)

    or non-null asserted (!!.) calls are allowed on a nullable receiver of type User? User user = service?.retrieveUser() String result = user.name.toUpperCase() @Inject var service: MyUserService? Null types
  42. @Inject MyUserService service User user = service?.retrieveUser() String result =

    user.name.toUpperCase() @Inject var service: MyUserService? User? user = service?.retrieveUser() String? result = user?.name?.toUpperCase() Null types
  43. user?.name?.toUpperCase() // String? user?.name ?: "unknown" // String user?.run {

    name = "Ada Lovelace" } // User? user!!.name // throws NPE! si user == null Null references navigation
  44. • Heavily inspired in Scala syntax • Classes are final

    by default • There is no new keyword • Properties syntax • Delegation for mixins
  45. class Animal @TupleConstructor class Cat extends Animal { String name

    } def felix = new Cat("Felix") def garfield = new Cat(name: "Garfield") open class Animal class Cat(val name: String) : Animal() val felix = Cat("Felix") val garfield = Cat(name = "Garfield") Class and objects definitions
  46. @TupleConstructor class Animal { String type } open class Animal(

    val type: String ) class Cat extends Animal { String name Cat(String name) { super("cat") this.name = name } } class Cat( val name: String ) : Animal("Cat") Class and objects definitions
  47. class Cat (val name: String) { String getCatName() { "meoow!

    ${name}" } } class Cat (val name: String) { val catName get() = "meoow! ${name}" } Properties for classes
  48. @Canonical @Immutable(withCopy=true) class Person { String name Integer age }

    data class Person ( val name: String, val age: Integer ) Immutable data
  49. sealed class Expression data class Num(val value: Int) : Expression

    data class Sum( val left: Expression, val right: Expression ) : Expression Sealed classes
  50. class MyData(val num: Int) { operator plus(other: MyData) = MyData(num

    + other.num) } MyData(10) + MyData(20) // MyData(30) Operator overloading
  51. class MyData(val num: Int) { infix fun times(other: Int) =

    MyData(num * other) } MyData(10) times 3 // MyData(30) Infix methods
  52. list.map { it * 2 } list.map { num ->

    num * 2 } fun transform(list: List<Int>, mapFn: (Int) -> Int) = list.map(mapFn) .filter { it % 2 == 0 } transform(myList) { it * 2 } Using closures in Kotlin
  53. Boolean hasZeros(Integer... ints) { ints.forEach { if (it == 0)

    return true } return false } fun hasZeros(vararg ints: Int): Boolean { ints.forEach { if (it == 0) return true } return false } hasZeros(0, 0, 0) hasZeros(0, 0, 0) // false // true Non-local returns
  54. inline fun myInlineFn(func: () -> Unit) { println("Before") func() println("Result

    ${result}") } fun main() { myInlineFn { return } } Function inlining
  55. inline fun myInlineFn(func: () -> Unit) { println("Before") func() println("Result

    ${result}") } fun main() { println("Before") return println("Result ${result}") } Function inlining
  56. Boolean hasZeros(Integer... ints) { ints.forEach { if (it == 0)

    return true } return false } fun hasZeros(vararg ints: Int): Boolean { ints.forEach { if (it == 0) return true } return false } Function inlining
  57. inline fun myInlineFn(func: () -> Unit) { println("Before") func() println("Result

    ${result}") } fun main() { myInlineFn { return } } Only allowed in inline functions Function inlining
  58. assert 123g instanceof BigInteger assert 42G instanceof BigInteger assert 1.423

    instanceof BigDecimal assert 42.0g instanceof BigDecimal assert 9203.10291G instanceof BigDecimal Groovy BigDecimal literals
  59. Integer.metaClass.asBig = { new BigDecimal(delegate) } Integer.metaClass.getG = { new

    BigDecimal(delegate) } fun Int.asBig() = BigDecimal(this) val Int.g get() = BigDecimal(this) Extension methods
  60. class AsBigExtension { static BigDecimal asBig(Integer num) { new BigDecimal(num)

    } static BigDecimal getG(Integer num) { new BigDecimal(num) } } use(AsBigExtension) { 10.asBig() } use(AsBigExtension) { 10.g } Groovy extension methods
  61. fun transform(myfn: Int.() -> Int): Int { val result =

    100 return result.myfn() } transform { println(this) // 100 return this * 2 // 200 } Extension method closures
  62. • Coroutines • Variance • Multiplatform (JS, Native, …) •

    Android • First class delegation • Reified generics • Annotation processors • Builders
  63. • AST’s & Macros (Compile time metaprogramming) • Dynamic needs

    (doh!) • Testing (Spock FTW) • Ecosystem (Grails, Gradle, GPars…) • Community Driven (vs company interests)
  64. • New and growing strong • Safer than Java (&

    Groovy) • Multiple platforms (Android, Native…) • Awesome tooling (who knew?) • Attracting people from Java, Scala and Groovy