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

Kotlin – your 2017 Java replacement

Kotlin – your 2017 Java replacement

Do you dream about life without unexpected nulls? Do you fancy a little bit of functional programming? Are you tired of Java ceremony?

Then maybe 2017 is The Right Moment to choose Kotlin! This JVM language brings you stuff like null-safety and immutability by default. Less boilerplate, more safety. And you can introduce it in your project class after class, module after module, without risky revolution.

I will show you pros and cons of Kotlin, based on last 8 months of my experience. In November 2016 I started a new backend project built on top of Gradle and Spring. Today I still do not regret the decision to write it in Kotlin. Let's see if I convince you! :-)

Paweł Barszcz

June 21, 2017
Tweet

More Decks by Paweł Barszcz

Other Decks in Programming

Transcript

  1. ?

  2. Vavr - Functional Java Done Right
 Grzegorz Piwowarek @ Confitura

    2016 more „classic” projects greenfield project
 solo on backend
 head full of ideas April
 2016 October
 2016 Kotlin here, Kotlin there
  3. stack Kotlin 1.0 –> 1.1 JUnit 4
 - AssertJ
 -

    JUnitParams Spring Boot 1.3 –> 1.5
 - web
 - security
 - jdbc
 - test
  4. 8 months later… Kotlin in the whole service team of

    2 devs 3 months on (real) production still no regret
  5. class Task( val id: TaskId, val name: String, private val

    priority: TaskPriority = NORMAL ) { fun isImportant() = when (priority) { HIGH -> true NORMAL, LOW -> false } fun duplicate(): Task { val newName = "$name (copy)" /* ... */ } } Task(name = "prepare a talk for Devoxx PL", id = TaskId.random())
  6. class Task( val id: TaskId, val name: String, private val

    priority: TaskPriority = NORMAL ) { fun isImportant() = when (priority) { HIGH -> true NORMAL, LOW -> false } fun duplicate(): Task { val newName = "$name (copy)" /* ... */ } } Task(name = "prepare a talk for Devoxx PL", id = TaskId.random()) name –> type
  7. class Task( val id: TaskId, val name: String, private val

    priority: TaskPriority = NORMAL ) { fun isImportant() = when (priority) { HIGH -> true NORMAL, LOW -> false } fun duplicate(): Task { val newName = "$name (copy)" /* ... */ } } Task(name = "prepare a talk for Devoxx PL", id = TaskId.random()) fields declared as
 constructor arguments
  8. class Task( val id: TaskId, val name: String, private val

    priority: TaskPriority = NORMAL ) { fun isImportant() = when (priority) { HIGH -> true NORMAL, LOW -> false } fun duplicate(): Task { val newName = "$name (copy)" /* ... */ } } Task(name = "prepare a talk for Devoxx PL", id = TaskId.random()) defaults
  9. class Task( val id: TaskId, val name: String, private val

    priority: TaskPriority = NORMAL ) { fun isImportant() = when (priority) { HIGH -> true NORMAL, LOW -> false } fun duplicate(): Task { val newName = "$name (copy)" /* ... */ } } Task(name = "prepare a talk for Devoxx PL", id = TaskId.random()) named parameters
  10. class Task( val id: TaskId, val name: String, private val

    priority: TaskPriority = NORMAL ) { fun isImportant() = when (priority) { HIGH -> true NORMAL, LOW -> false } fun duplicate(): Task { val newName = "$name (copy)" /* ... */ } } Task(name = "prepare a talk for Devoxx PL", id = TaskId.random()) String interpolation
  11. class Task( val id: TaskId, val name: String, private val

    priority: TaskPriority = NORMAL ) { fun isImportant() = when (priority) { HIGH -> true NORMAL, LOW -> false } fun duplicate(): Task { val newName = "$name (copy)" /* ... */ } } Task(name = "prepare a talk for Devoxx PL", id = TaskId.random()) no type, because…
 type inference
  12. class Task( val id: TaskId, val name: String, private val

    priority: TaskPriority = NORMAL ) { fun isImportant() = when (priority) { HIGH -> true NORMAL, LOW -> false } fun duplicate(): Task { val newName = "$name (copy)" /* ... */ } } Task(name = "prepare a talk for Devoxx PL", id = TaskId.random()) smart `when` compiles
 if all options are covered
  13. class Task( val id: TaskId, val name: String, private val

    priority: TaskPriority = NORMAL ) { fun isImportant() = when (priority) { HIGH -> true NORMAL, LOW -> false } fun duplicate(): Task { val newName = "$name (copy)" /* ... */ } } Task(name = "prepare a talk for Devoxx PL", id = TaskId.random()) expressions everywhere
 `if`, `when`, `try`…
  14. class Task( val id: TaskId, val name: String, private val

    priority: TaskPriority = NORMAL ) { fun isImportant() = when (priority) { HIGH -> true NORMAL, LOW -> false } fun duplicate(): Task { val newName = "$name (copy)" /* ... */ } } Task(name = "prepare a talk for Devoxx PL", id = TaskId.random()) function as a single expression
  15. var map1: Map<String, Int> = mapOf("A" to 1) val map2:

    MutableMap<String, Int> = mutableMapOf("B" to 2) fun whatever() { map1 += ("A" to 111) map2["B"] = 222 } modification
 mutable collections only shorter = immutable
  16. var map1: Map<String, Int> = mapOf("A" to 1) val map2:

    MutableMap<String, Int> = mutableMapOf("B" to 2) fun whatever() { map1 += ("A" to 111) map2["B"] = 222 } reassignment
 `var` only var ≠ val
  17. data class Color( val red: Pigment, val green: Pigment, val

    blue: Pigment, val alpha: Alpha = Alpha.none() ) { fun transparent() = copy(alpha = Alpha.medium()) } out of the box: - copy
  18. data class Color( val red: Pigment, val green: Pigment, val

    blue: Pigment, val alpha: Alpha = Alpha.none() ) { fun transparent() = copy(alpha = Alpha.medium()) } val (red, green, blue, alpha) = myColor out of the box: - copy - destructuring
  19. data class Color( val red: Pigment, val green: Pigment, val

    blue: Pigment, val alpha: Alpha = Alpha.none() ) { fun transparent() = copy(alpha = Alpha.medium()) } out of the box: - copy - destructuring - equal & hashCode - toString
  20. everything is final • Java –> „final” on demand
 Kotlin

    –> „open” on demand • Spring, Mockito, … how to proxy? • kotlin-allopen / kotlin-spring
 blog.jetbrains.com/kotlin/2016/12/kotlin-1-0-6-is-here
  21. buildscript { dependencies { classpath "org.jetbrains.kotlin:kotlin-allopen:1.1.2-5" } } apply plugin:

    "kotlin-allopen" allOpen { annotation("com.your.Annotation") } everything is final
  22. val project: Project? = projectRepository.findProjectBy(projectId) if (project == null) {

    throw ProjectNotFound() } return project.tasks compiler is smart
  23. override fun configure(webSecurity: WebSecurity) { webSecurity .ignoring() .mvcMatchers(/* ... */)

    } override fun configure(webSecurity: WebSecurity?) { webSecurity!! .ignoring() .mvcMatchers(/* ... */) } override fun configure4(webSecurity: WebSecurity?) { webSecurity ?.ignoring() ?.mvcMatchers(/* ... */) } when null comes from Java
  24. override fun configure(webSecurity: WebSecurity) { webSecurity .ignoring() .mvcMatchers(/* ... */)

    } override fun configure(webSecurity: WebSecurity?) { webSecurity!! .ignoring() .mvcMatchers(/* ... */) } override fun configure4(webSecurity: WebSecurity?) { webSecurity ?.ignoring() ?.mvcMatchers(/* ... */) } when null comes from Java
  25. override fun configure(webSecurity: WebSecurity) { webSecurity .ignoring() .mvcMatchers(/* ... */)

    } override fun configure(webSecurity: WebSecurity?) { webSecurity!! .ignoring() .mvcMatchers(/* ... */) } override fun configure4(webSecurity: WebSecurity?) { webSecurity ?.ignoring() ?.mvcMatchers(/* ... */) } when null comes from Java
  26. override fun configure(webSecurity: WebSecurity) { webSecurity .ignoring() .mvcMatchers(/* ... */)

    } override fun configure(webSecurity: WebSecurity?) { webSecurity!! .ignoring() .mvcMatchers(/* ... */) } override fun configure4(webSecurity: WebSecurity?) { webSecurity ?.ignoring() ?.mvcMatchers(/* ... */) } when null comes from Java
  27. class ActiveProjectsJson( val projects: List<ProjectJson> ) class ArchivedProjectsJson( val archivedProjects:

    List<ProjectJson> ) class ProjectJson( val id: String, val name: String, val color: String ) class ProjectUpdateJson( val name: String?, val color: String? ) …request/response bodies?
  28. class ActiveProjectsJson( val projects: List<ProjectJson> ) class ArchivedProjectsJson( val archivedProjects:

    List<ProjectJson> ) class ProjectJson( val id: String, val name: String, val color: String ) class ProjectUpdateJson( val name: String?, val color: String? ) …request/response bodies?
  29. class ActiveProjectsJson( val projects: List<ProjectJson> ) class ArchivedProjectsJson( val archivedProjects:

    List<ProjectJson> ) class ProjectJson( val id: String, val name: String, val color: String ) class ProjectUpdateJson( val name: String?, val color: String? ) …request/response bodies? in one file
 if you prefer
  30. jsonObject( "name" to "John", "age" to 29, "phoneNumbers" to jsonArray(

    jsonObject( "type" to "personal", "number" to "123 456 789" ), jsonObject( "type" to "office", "number" to "987 654 321" ) ) ).toString() Kotson
  31. …SQL? • me & Hibernate = • me & tables

    coupled with domain entities = • me & types = ❤
  32. object ProjectsTable : Table("projects") { val id = long("id").autoIncrement().primaryKey() val

    ownerId = (long("owner") references UsersTable.id) val name = varchar("name", 50) val isArchived = bool("is_archived").default(false) } Exposed
  33. ProjectsTable .select { ProjectsTable.id eq projectId } .map { row

    -> Project( id = row[ProjectsTable.id], name = row[ProjectsTable.name], isArchived = row[ProjectsTable.isArchived] ) } .firstOrNull() Exposed
  34. return Try { fetchProjectsOverHttp() // may throw an exception }.handle

    { throw ConnectionFailed() }.onEach { response -> logger.info { response } }.flatMap { response -> Try { parse(response) } .handle { throw ResponseParsingFailed() } }.fold({ result -> result.projects },
 { exception -> throw exception })
  35. return Try { fetchProjectsOverHttp() // may throw an exception }.handle

    { throw ConnectionFailed() }.onEach { response -> logger.info { response } }.flatMap { response -> Try { parse(response) } .handle { throw ResponseParsingFailed() } }.fold({ result -> result.projects },
 { exception -> throw exception }) wrap value in Try.Sucess
 wrap exception in Try.Failure
  36. return Try { fetchProjectsOverHttp() // may throw an exception }.handle

    { throw ConnectionFailed() }.onEach { response -> logger.info { response } }.flatMap { response -> Try { parse(response) } .handle { throw ResponseParsingFailed() } }.fold({ result -> result.projects },
 { exception -> throw exception }) do something
 with Try.Failure
  37. return Try { fetchProjectsOverHttp() // may throw an exception }.handle

    { throw ConnectionFailed() }.onEach { response -> logger.info { response } }.flatMap { response -> Try { parse(response) } .handle { throw ResponseParsingFailed() } }.fold({ result -> result.projects },
 { exception -> throw exception }) do something
 with Try.Success
  38. return Try { fetchProjectsOverHttp() // may throw an exception }.handle

    { throw ConnectionFailed() }.onEach { response -> logger.info { response } }.flatMap { response -> Try { parse(response) } .handle { throw ResponseParsingFailed() } }.fold({ result -> result.projects },
 { exception -> throw exception }) nested Try logic
 inside `flatMap`
  39. return Try { fetchProjectsOverHttp() // may throw an exception }.handle

    { throw ConnectionFailed() }.onEach { response -> logger.info { response } }.flatMap { response -> Try { parse(response) } .handle { throw ResponseParsingFailed() } }.fold({ result -> result.projects },
 { exception -> throw exception }) unwrap value or exception
  40. • maintained by JetBrains • easy to start with •

    nice syntax, immutability, data classes • works well with Spring • Kotson, Exposed, funKTionale • null-safety • low risk, pragmatic approach
  41. • maintained by JetBrains • easy to start with •

    nice syntax, immutability, data classes • works well with Spring • Kotson, Exposed, funKTionale • null-safety • low risk, pragmatic approach
  42. want more? • type aliases • couroutines (async/await/yield) • lazy

    sequences • sealed classes • gradle-script-kotlin • …
  43. • Spring Boot 1.5 + Kotlin + customized Exposed (blog

    post + git repo)
 spring.io/blog/2016/03/20/a-geospatial-messenger-with-kotlin-spring-boot-and-postgresql • functional Spring 5 + Kotlin (blog post + git repo)
 spring.io/blog/2017/01/04/introducing-kotlin-support-in-spring-framework-5-0 • reference 
 kotlinlang.org/docs/reference • links, libs, blogs…
 kotlin.link • community 
 slack.kotlinlang.org • these slides 
 speakerdeck.com/nkoder/kotlin-your-2017-java-replacement