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

Idiomatic_Kotlin__third_edition.pdf

 Idiomatic_Kotlin__third_edition.pdf

Anton Arhipov

October 16, 2021
Tweet

More Decks by Anton Arhipov

Other Decks in Programming

Transcript

  1. Idiomatic - using, containing, or denoting expressions that are natural

    to a native speaker Commonly accepted style Effective use of features
  2. fun main() { doSomething() } fun doSomething() { doMoreStuff( :

    : finishWork) } fun doMoreStuff(callback: () -> Unit) { callback() } fun finishWork() { TODO("Not implemented yet") }
  3. fun main() { doSomething() } fun doSomething() { doMoreStuff( :

    : finishWork) } fun doMoreStuff(callback: () -> Unit) { callback() } fun finishWork() { TODO("Not implemented yet") }
  4. fun main() { doSomething() } fun doSomething() { doMoreStuff( :

    : finishWork) } fun doMoreStuff(callback: () -> Unit) { callback() } fun finishWork() { TODO("Not implemented yet") }
  5. fun main() { doSomething() } fun doSomething() { doMoreStuff( :

    : finishWork) } fun doMoreStuff(callback: () -> Unit) { callback() } fun finishWork() { TODO("Not implemented yet") } Just functions, no classes!
  6. Don’t create classes just to hold functions class StringUtils {

    companion object { fun isPhoneNumber(s: String) = s.length = = 7 & & s.all { it.isDigit() } } }
  7. Don’t create classes just to hold functions class StringUtils {

    companion object { fun isPhoneNumber(s: String) = s.length = = 7 & & s.all { it.isDigit() } } } object StringUtils { fun isPhoneNumber(s: String) = s.length = = 7 & & s.all { it.isDigit() } }
  8. Don’t create classes just to hold functions class StringUtils {

    companion object { fun isPhoneNumber(s: String) = s.length = = 7 & & s.all { it.isDigit() } } } object StringUtils { fun isPhoneNumber(s: String) = s.length = = 7 & & s.all { it.isDigit() } } fun isPhoneNumber(s: String) = s.length == 7 && s.all { it.isDigit() }
  9. Use extension functions class StringUtils { companion object { fun

    isPhoneNumber(s: String) = s.length = = 7 & & s.all { it.isDigit() } } } object StringUtils { fun isPhoneNumber(s: String) = s.length = = 7 & & s.all { it.isDigit() } } fun isPhoneNumber(s: String) = s.length == 7 && s.all { it.isDigit() } fun String.isPhoneNumber() = length == 7 && all { it.isDigit() }
  10. Extension or a member? https://kotlinlang.org/docs/coding-conventions.html#extension-functions Use extension functions liberally. Minimize

    API pollution, restrict the visibility. As necessary, use local extension functions, member extension functions, or top-level extension functions with private visibility.
  11. +

  12. fun findMessageById(id: String) = db.query( "select * from messages where

    id = ?", RowMapper { rs, _ -> Message(rs.getString("id"), rs.getString("text")) }, id )
  13. fun findMessageById(id: String) = db.query( "select * from messages where

    id = ?", RowMapper { rs, _ -> Message(rs.getString("id"), rs.getString("text")) }, id ) @Override public <T> List<T> query(String sql, RowMapper<T> rowMapper, @Nullable Object . .. args) throws DataAccessException { return result(query(sql, args, new RowMapperResultSetExtractor < > (rowMapper))); }
  14. fun findMessageById(id: String) = db.query( "select * from messages where

    id = ?", RowMapper { rs, _ -> Message(rs.getString("id"), rs.getString("text")) }, id )
  15. fun findMessageById(id: String) = db.query( "select * from messages where

    id = ?", id, RowMapper { rs, _ -> Message(rs.getString("id"), rs.getString("text")) } )
  16. fun findMessageById(id: String) = db.query( "select * from messages where

    id = ?", id, { rs, _ -> Message(rs.getString("id"), rs.getString("text")) } )
  17. fun findMessageById(id: String) = db.query( "select * from messages where

    id = ?", id) { rs, _ -> Message(rs.getString("id"), rs.getString("text")) }
  18. fun findMessageById(id: String) = db.query( "select * from messages where

    id = ?", id) { rs, _ -> Message(rs.getString("id"), rs.getString("text")) }
  19. fun findMessageById(id: String) = db.query( "select * from messages where

    id = ?", id) { rs, _ -> Message(rs.getString("id"), rs.getString("text")) } fun <T> JdbcOperations.query(sql: String, vararg args: Any, function: (ResultSet, Int) -> T): List<T> = query(sql, RowMapper { rs, i -> function(rs, i) }, *args)
  20. val dataSource = BasicDataSource( ) dataSource.driverClassName = "com.mysql.jdbc.Driver" dataSource.url =

    "jdbc:mysql://domain:3309/db" dataSource.username = "username" dataSource.password = "password" dataSource.maxTotal = 40 dataSource.maxIdle = 40 dataSource.minIdle = 4
  21. val dataSource = BasicDataSource( ) dataSource.driverClassName = "com.mysql.jdbc.Driver" dataSource.url =

    "jdbc:mysql://domain:3309/db" dataSource.username = "username" dataSource.password = "password" dataSource.maxTotal = 40 dataSource.maxIdle = 40 dataSource.minIdle = 4 val dataSource = BasicDataSource().apply { driverClassName = "com.mysql.jdbc.Driver" url = "jdbc:mysql://domain:3309/db" username = "username" password = "password" maxTotal = 40 maxIdle = 40 minIdle = 4 }
  22. val dataSource = BasicDataSource().apply { driverClassName = "com.mysql.jdbc.Driver" url =

    "jdbc:mysql://domain:3309/db" username = "username" password = "password" maxTotal = 40 maxIdle = 40 minIdle = 4 } public inline fun <T> T.apply(block: T.() -> Unit): T { block() return this }
  23. val dataSource = BasicDataSource().apply { driverClassName = "com.mysql.jdbc.Driver" url =

    "jdbc:mysql://domain:3309/db" username = "username" password = "password" maxTotal = 40 maxIdle = 40 minIdle = 4 } public inline fun <T> T.apply(block: T.() -> Unit): T { block() return this } Lambda with receiver
  24. val order = retrieveOrder() if (order != null){ processCustomer(order.customer) }

    retrieveOrder() ?. let { processCustomer(it.customer) } retrieveOrder() ?. customer ?. let { :: processCustomer } or ?.let
  25. val order = retrieveOrder() if (order != null){ processCustomer(order.customer) }

    retrieveOrder() ?. let { processCustomer(it.customer) } No extra variable retrieveOrder() ?. let { processCustomer(it.customer) } retrieveOrder() ?. customer ?. let { :: processCustomer } or ?.let
  26. fun makeDir(path: String) = path.let { File(it) }.also { it.mkdirs()

    } fun makeDir(path: String) : File { val file = File(path) file.mkdirs() return file } Don’t overuse the scope functions! This is simpler!
  27. fun makeDir(path: String) = path.let { File(it) }.also { it.mkdirs()

    } fun makeDir(path: String) : File { val file = File(path) file.mkdirs() return file } Don’t overuse the scope functions! This is simpler! fun makeDir(path: String) = File(path).also { it.mkdirs() } OK, this one is actually fi ne :)
  28. fun find(name: String){ find(name, true) } fun find(name: String, recursive:

    Boolean){ } fun find(name: String, recursive: Boolean = true){ } Default argument value Function overloading
  29. fun find(name: String){ find(name, true) } fun find(name: String, recursive:

    Boolean){ } fun find(name: String, recursive: Boolean = true){ } fun main() { find("myfile.txt") } Default argument value Function overloading
  30. class Figure( val width: Int = 1, val height: Int

    = 1, val depth: Int = 1, color: Color = Color.BLACK, description: String = "This is a 3d figure", ) Figure(Color.RED, "Red figure")
  31. class Figure( val width: Int = 1, val height: Int

    = 1, val depth: Int = 1, color: Color = Color.BLACK, description: String = "This is a 3d figure", ) Figure(Color.RED, "Red figure") Compilation error
  32. class Figure( val width: Int = 1, val height: Int

    = 1, val depth: Int = 1, color: Color = Color.BLACK, description: String = "This is a 3d figure", ) Figure(color = Color.RED, description = "Red figure")
  33. Default argument values diminish the need for overloading in most

    cases. Named parameters is a necessary tool for working with default argument values
  34. fun adjustSpeed(weather: Weather): Drive { val result: Drive if (weather

    is Rainy) { result = Safe() } else { result = Calm() } return result }
  35. fun adjustSpeed(weather: Weather): Drive { val result: Drive if (weather

    is Rainy) { result = Safe() } else { result = Calm() } return result }
  36. fun adjustSpeed(weather: Weather): Drive { val result: Drive = if

    (weather is Rainy) { Safe() } else { Calm() } return result }
  37. fun adjustSpeed(weather: Weather): Drive { val result: Drive = if

    (weather is Rainy) { Safe() } else { Calm() } return result }
  38. fun adjustSpeed(weather: Weather): Drive = ... fun adjustSpeed(weather: Weather) =

    ... For public API, keep the return type in the signature For private API it is generally OK to use type inference
  39. abstract class Weather class Sunny : Weather() class Rainy :

    Weather() fun adjustSpeed(weather: Weather) = when (weather) { is Rainy -> Safe() else -> Calm() }
  40. sealed class Weather class Sunny : Weather() class Rainy :

    Weather() fun adjustSpeed(weather: Weather) = when (weather) { is Rainy -> Safe() / / else -> Calm() }
  41. sealed class Weather class Sunny : Weather() class Rainy :

    Weather() fun adjustSpeed(weather: Weather) = when (weather) { is Rainy -> Safe() / / else -> Calm() }
  42. sealed class Weather class Sunny : Weather() class Rainy :

    Weather() fun adjustSpeed(weather: Weather) = when (weather) { is Rainy -> Safe() is Sunny -> TODO() }
  43. sealed class Weather class Sunny : Weather() class Rainy :

    Weather() fun adjustSpeed(weather: Weather) = when (weather) { is Rainy -> Safe() is Sunny -> TODO() } Use expressions! Use when as expression body Use sealed classes with when
  44. Use try as expression body fun tryParse(number: String) : Int?

    { try { return Integer.parseInt(number) } catch (e: NumberFormatException) { return null } }
  45. Use try as expression body fun tryParse(number: String) = try

    { Integer.parseInt(number) } catch (e: NumberFormatException) { null }
  46. Use try as expression fun tryParse(number: String) : Int? {

    val n = try { Integer.parseInt(number) } catch (e: NumberFormatException) { null } println(n) return n }
  47. class Nullable { fun someFunction(){} } fun createNullable(): Nullable? =

    null fun main() { val n: Nullable? = createNullable() n.someFunction() }
  48. class Nullable { fun someFunction(){} } fun createNullable(): Nullable? =

    null fun main() { val n: Nullable? = createNullable() n.someFunction() }
  49. Consider using null-safe call val order = retrieveOrder() if (order

    == null || order.customer = = null || order.customer.address == null){ throw IllegalArgumentException("Invalid Order") } val city = order.customer.address.city
  50. Consider using null-safe call val order = retrieveOrder() val city

    = order ?. customer ? . address ?. city ?: throw IllegalArgumentException("Invalid Order")
  51. Avoid not-null assertions !! val order = retrieveOrder() val city

    = order !! .customer !! .address !! .city “You may notice that the double exclamation mark looks a bit rude: it’s almost like you’re yelling at the compiler. This is intentional.” - Kotlin in Action
  52. Avoid not-null assertions !! class MyTest { class State(val data:

    String) private var state: State? = null @BeforeEach fun setup() { state = State("abc") } @Test fun foo() { assertEquals("abc", state !! .data) } }
  53. Avoid not-null assertions !! class MyTest { class State(val data:

    String) private var state: State? = null @BeforeEach fun setup() { state = State("abc") } @Test fun foo() { assertEquals("abc", state !! .data) } } class MyTest { class State(val data: String) private lateinit var state: State @BeforeEach fun setup() { state = State("abc") } @Test fun foo() { assertEquals("abc", state.data) } } - use lateinit
  54. Use elvis operator as return and throw class Person(val name:

    String?, val age: Int?) fun processPerson(person: Person) { val name = person.name if (name = = null) throw IllegalArgumentException("Named required") val age = person.age if (age == null) return println("$name: $age") }
  55. Use elvis operator as return and throw class Person(val name:

    String?, val age: Int?) fun processPerson(person: Person) { val name = person.name if (name = = null) throw IllegalArgumentException("Named required") val age = person.age if (age == null) return println("$name: $age") }
  56. Use elvis operator as return and throw class Person(val name:

    String?, val age: Int?) fun processPerson(person: Person) { val name = person.name if (name = = null) throw IllegalArgumentException("Named required") val age = person.age if (age == null) return println("$name: $age") }
  57. Use elvis operator as return and throw class Person(val name:

    String?, val age: Int?) fun processPerson(person: Person) { val name = person.name ? : throw IllegalArgumentException("Named required") val age = person.age ?: return println("$name: $age") }
  58. Consider using safe cast for type checking override fun equals(other:

    Any?) : Boolean { val command = other as Command return command.id == id }
  59. Consider using safe cast for type checking override fun equals(other:

    Any?) : Boolean { val command = other as Command return command.id == id } override fun equals(other: Any?) : Boolean { return (other as? Command) ?. id == id }
  60. class Version(val major: Int, val minor: Int): Comparable<Version> { override

    fun compareTo(other: Version): Int { if (this.major != other.major) { return this.major - other.major } return this.minor - other.minor } } fun main() { val versionRange = Version(1, 11) . . Version(1, 30) println(Version(0, 9) in versionRange) println(Version(1, 20) in versionRange) } Comparable range
  61. class Version(val major: Int, val minor: Int): Comparable<Version> { override

    fun compareTo(other: Version): Int { if (this.major != other.major) { return this.major - other.major } return this.minor - other.minor } } fun main() { val versionRange = Version(1, 11) . . Version(1, 30) println(Version(0, 9) in versionRange) println(Version(1, 20) in versionRange) } Comparable range public operator fun <T : Comparable<T > > T.rangeTo(that: T): ClosedRange<T> = ComparableRange(this, that)
  62. class Version(val major: Int, val minor: Int): Comparable<Version> { override

    fun compareTo(other: Version): Int { if (this.major != other.major) { return this.major - other.major } return this.minor - other.minor } } fun main() { val versionRange = Version(1, 11) . . Version(1, 30) println(Version(0, 9) in versionRange) println(Version(1, 20) in versionRange) } Comparable range public operator fun <T : Comparable<T > > T.rangeTo(that: T): ClosedRange<T> = ComparableRange(this, that)
  63. Ranges in loops fun main(args: Array<String>) { for (i in

    0 .. args.size - 1) { println("$i: ${args[i]}") } }
  64. Ranges in loops fun main(args: Array<String>) { for (i in

    0 .. args.size - 1) { println("$i: ${args[i]}") } }
  65. Ranges in loops fun main(args: Array<String>) { for (i in

    0 .. args.size - 1) { println("$i: ${args[i]}") } } for (i in 0 until args.size) { println("$i: ${args[i]}") }
  66. Ranges in loops fun main(args: Array<String>) { for (i in

    0 .. args.size - 1) { println("$i: ${args[i]}") } } for (i in 0 until args.size) { println("$i: ${args[i]}") } for (i in args.indices) { println("$i: ${args[i]}") }
  67. Ranges in loops fun main(args: Array<String>) { for (i in

    0 .. args.size - 1) { println("$i: ${args[i]}") } } for (i in 0 until args.size) { println("$i: ${args[i]}") } for (i in args.indices) { println("$i: ${args[i]}") } for ((i, arg) in args.withIndex()) { println("$i: $arg") }
  68. Return multiple values using data classes fun namedNum(): Pair<Int, String>

    = 1 to "one" // same but shorter fun namedNum2() = 1 to "one" fun main(args: Array<String>) { val pair = namedNum() val number = pair.first val name = pair.second }
  69. Return multiple values using data classes fun namedNum(): Pair<Int, String>

    = 1 to "one" // same but shorter fun namedNum2() = 1 to "one" fun main(args: Array<String>) { val pair = namedNum() val number = pair.first val name = pair.second } data class GameResult( val rank: Int, val name: String ) fun namedNum() = GameResult(1, "Player 1") fun main(args: Array<String>) { val (rank, name) = namedNum() println("$name, rank $rank") }
  70. Return multiple values using data classes data class GameResult( val

    rank: Int, val name: String ) fun namedNum() = GameResult(1, "Player 1") fun main(args: Array<String>) { val (rank, name) = namedNum() println("$name, rank $rank") } GameResult var1 = namedNum(); int var2 = var1.component1(); String var3 = var1.component2();
  71. Destructuring in loops fun printMap(map: Map<String, String>) { for (item

    in map.entries) { println("${item.key} -> ${item.value}") } }
  72. Destructuring in loops fun printMap(map: Map<String, String>) { for (item

    in map.entries) { println("${item.key} -> ${item.value}") } } fun printMap(map: Map<String, String>) { for ((key, value) in map) { println("$key -> $value") } }
  73. Destructuring in lists data class NameExt( val name: String, val

    ext: String? ) fun splitNameExt(filename: String): NameExt { if ('.' in filename) { val parts = filename.split('.', limit = 2) return NameExt(parts[0], parts[1]) } return NameExt(filename, null) } fun splitNameAndExtension(filename: String): NameExt { if ('.' in filename) { val (name, ext) = filename.split('.', limit = 2) return NameExt(name, ext) } return NameExt(filename, null) }
  74. abcd bcde cdef defg xya xyb xyc Group 1: Group

    2: Count the total of characters that are present on each line in every group of strings
  75. abcd bcde cdef defg xya xyb xyc Group 1: Group

    2: Count the total of characters that are present on each line in every group of strings
  76. abcd bcde cdef defg xya xyb xyc Group 1: Group

    2: Count the total of characters that are present on each line in every group of strings {d} {xy}
  77. abcd bcde cdef defg xya xyb xyc Group 1: Group

    2: Count the total of characters that are present on each line in every group of strings {d} {xy} count = 1 count = 2
  78. abcd bcde cdef defg xya xyb xyc Group 1: Group

    2: Count the total of characters that are present on each line in every group of strings {d} {xy} count = 1 count = 2 Total = 3
  79. val input = """ abcd bcde cdef defg xya xyb

    xyc """.trimIndent() val groups: List<String> = input.split("\n\n")
  80. val input = """ abcd bcde cdef defg xya xyb

    xyc """.trimIndent() val groups: List<String> = input.split("\n\n")
  81. val input = """ abcd bcde cdef defg xya xyb

    xyc """.trimIndent() val groups: List<String> = input.split("\n\n") var total = 0 for (group in groups) { val listOfSets: List<Set<Char >> = group.split("\n").map(String :: toSet) var result = listOfSets.first() for (set in listOfSets) { result = result intersect set } total += result.count() }
  82. val input = """ abcd bcde cdef defg xya xyb

    xyc """.trimIndent() val groups: List<String> = input.split("\n\n") var total = 0 for (group in groups) { val listOfSets: List<Set<Char >> = group.split("\n").map(String :: toSet) var result = listOfSets.first() for (set in listOfSets) { result = result intersect set } total += result.count() }
  83. val input = """ abcd bcde cdef defg xya xyb

    xyc """.trimIndent() val groups: List<String> = input.split("\n\n") var total = 0 for (group in groups) { val listOfSets: List<Set<Char >> = group.split("\n").map(String :: toSet) var result = listOfSets.first() for (set in listOfSets) { result = result intersect set } total += result.count() }
  84. val input = """ abcd bcde cdef defg xya xyb

    xyc """.trimIndent() val groups: List<String> = input.split("\n\n") var total = 0 for (group in groups) { val listOfSets: List<Set<Char >> = group.split("\n").map(String :: toSet) var result = listOfSets.first() for (set in listOfSets) { result = result intersect set } total += result.count() }
  85. val input = """ abcd bcde cdef defg xya xyb

    xyc """.trimIndent() val groups: List<String> = input.split("\n\n") var total = 0 for (group in groups) { val listOfSets: List<Set<Char >> = group.split("\n").map(String :: toSet) var result = listOfSets.first() for (set in listOfSets) { result = result intersect set } total += result.count() } Transforming data
  86. val input = """ abcd bcde cdef defg xya xyb

    xyc """.trimIndent() val groups: List<String> = input.split("\n\n") var total = 0 for (group in groups) { val listOfSets: List<Set<Char >> = group.split("\n").map(String :: toSet) var result = listOfSets.first() for (set in listOfSets) { result = result intersect set } total += result.count() } Transforming data Calculating the result
  87. val groups: List<String> = input.split("\n\n") // List<List<Set<Char >>> val step1

    = groups.map { it.split("\n").map(String :: toSet) } Transforming data
  88. val groups: List<String> = input.split("\n\n") // List<List<Set<Char >>> val step1

    = groups.map { it.split("\n").map(String :: toSet) } Transforming data
  89. val groups: List<String> = input.split("\n\n") // List<List<Set<Char >>> val step1

    = groups.map { it.split("\n").map(String :: toSet) } Transforming data
  90. val groups: List<String> = input.split("\n\n") // List<List<Set<Char >>> val step1

    = groups.map { it.split("\n").map(String :: toSet) } Transforming data
  91. val groups: List<String> = input.split("\n\n") // List<List<Set<Char >>> val step1

    = groups.map { it.split("\n").map(String :: toSet) } Transforming data
  92. val groups: List<String> = input.split("\n\n") // List<List<Set<Char >>> val step1

    = groups.map { it.split("\n").map(String :: toSet) } val step2 = step1.sumOf { it.reduce { a, b - > a intersect b }.count() } Transforming data Calculating the result
  93. val groups: List<String> = input.split("\n\n") // List<List<Set<Char >>> val step1

    = groups.map { it.split("\n").map(String :: toSet) } val step2 = step1.sumOf { it.reduce { a, b - > a intersect b }.count() } Transforming data Calculating the result
  94. val groups: List<String> = input.split("\n\n") // List<List<Set<Char >>> val step1

    = groups.map { it.split("\n").map(String :: toSet) } val step2 = step1.sumOf { it.reduce { a, b - > a intersect b }.count() } Transforming data Calculating the result
  95. val groups: List<String> = input.split("\n\n") // List<List<Set<Char >>> val step1

    = groups.map { it.split("\n").map(String :: toSet) } val step2 = step1.sumOf { it.reduce { a, b - > a intersect b }.count() } Transforming data Calculating the result
  96. val groups: List<String> = input.split("\n\n") // List<List<Set<Char >>> val step1

    = groups.map { it.split("\n").map(String :: toSet) } val step2 = step1.sumOf { it.reduce { a, b - > a intersect b }.count() } Transforming data Calculating the result
  97. val groups: List<String> = input.split("\n\n") // List<List<Set<Char >>> val step1

    = groups.map { it.split("\n").map(String :: toSet) } val step2 = step1.sumOf { it.reduce { a, b - > a intersect b }.count() } Transforming data Calculating the result groups.map { group -> group.split(nl).map(String :: toSet) }.sumOf { answerSets -> answerSets.reduce { a, b -> a intersect b }.count() }
  98. Select objects by type with filterIsInstance fun findAllStrings(objects: List<Any>) =

    objects.filter { it is String } fun findAllStrings(objects: List<Any>) = objects.filterIsInstance<String>()
  99. Select objects by type with filterIsInstance fun findAllStrings(objects: List<Any>) :

    List<Any> = objects.filter { it is String } fun findAllStrings(objects: List<Any>) : List<String> = objects.filterIsInstance<String>()
  100. compareBy compares by multiple keys class Person( val name: String,

    val age: Int ) fun sortPersons(persons: List<Person>) = persons.sortedWith(Comparator<Person> { person1, person2 -> val rc = person1.name.compareTo(person2.name) if (rc != 0) rc else person1.age - person2.age })
  101. compareBy compares by multiple keys class Person( val name: String,

    val age: Int ) fun sortPersons(persons: List<Person>) = persons.sortedWith(Comparator<Person> { person1, person2 -> val rc = person1.name.compareTo(person2.name) if (rc != 0) rc else person1.age - person2.age }) fun sortPersons(persons: List<Person>) = persons.sortedWith(compareBy(Person :: name, Person : : age))
  102. groupBy to group elements class Request( val url: String, val

    remoteIP: String, val timestamp: Long ) fun analyzeLog(log: List<Request>) { val map = mutableMapOf<String, MutableList<Request >> () for (request in log) { map.getOrPut(request.url) { mutableListOf() } .add(request) } }
  103. groupBy to group elements class Request( val url: String, val

    remoteIP: String, val timestamp: Long ) fun analyzeLog(log: List<Request>) { val map = mutableMapOf<String, MutableList<Request >> () for (request in log) { map.getOrPut(request.url) { mutableListOf() } .add(request) } } fun analyzeLog(log: List<Request>) { val map = log.groupBy(Request :: url) }
  104. Use coerceIn to ensure numbers in range fun updateProgress(value: Int)

    { val actualValue = when { value < 0 - > 0 value > 100 -> 100 else - > value } } fun updateProgress(value: Int) { val actualValue = value.coerceIn(0, 100) }
  105. Initializing objects with apply val dataSource = BasicDataSource( ) dataSource.driverClassName

    = "com.mysql.jdbc.Driver" dataSource.url = "jdbc:mysql://domain:3309/db" dataSource.username = "username" dataSource.password = "password" dataSource.maxTotal = 40 dataSource.maxIdle = 40 dataSource.minIdle = 4 val dataSource = BasicDataSource().apply { driverClassName = "com.mysql.jdbc.Driver" url = "jdbc:mysql://domain:3309/db" username = "username" password = "password" maxTotal = 40 maxIdle = 40 minIdle = 4 }
  106. Initializing objects with apply final ClientBuilder builder = new ClientBuilder();

    builder.setFirstName("Anton"); builder.setLastName("Arhipov"); final TwitterBuilder twitterBuilder = new TwitterBuilder(); twitterBuilder.setHandle("@antonarhipov"); builder.setTwitter(twitterBuilder.build()); final CompanyBuilder companyBuilder = new CompanyBuilder(); companyBuilder.setName("JetBrains"); companyBuilder.setCity("Tallinn"); builder.setCompany(companyBuilder.build()); final Client client = builder.build(); System.out.println("Created client is: " + client);
  107. Initializing objects with apply val builder = ClientBuilder() builder.firstName =

    "Anton" builder.lastName = "Arhipov" val twitterBuilder = TwitterBuilder() twitterBuilder.handle = "@antonarhipov" builder.twitter = twitterBuilder.build() val companyBuilder = CompanyBuilder() companyBuilder.name = "JetBrains" companyBuilder.city = "Tallinn" builder.company = companyBuilder.build() val client = builder.build() println("Created client is: $client")
  108. Initializing objects with apply val builder = ClientBuilder() builder.firstName =

    "Anton" builder.lastName = "Arhipov" val twitterBuilder = TwitterBuilder() twitterBuilder.handle = "@antonarhipov" builder.twitter = twitterBuilder.build() val companyBuilder = CompanyBuilder() companyBuilder.name = "JetBrains" companyBuilder.city = "Tallinn" builder.company = companyBuilder.build() val client = builder.build() println("Created client is: $client") val client = ClientBuilder().apply { firstName = "Anton" lastName = "Arhipov" twitter = TwitterBuilder().apply { handle = "@antonarhipov" }.build() company = CompanyBuilder().apply { name = "JetBrains" city = "Tallinn" }.build() }.build() println("Created client is: $client")
  109. buildString //Java String name = "Joe"; StringBuilder sb = new

    StringBuilder(); for (int i = 0; i < 5; i++) { sb.append("Hello, "); sb.append(name); sb.append("!\n"); } System.out.println(sb); //Kotlin val name = "Joe" val s = buildString { repeat(5) { append("Hello, ") append(name) appendLine("!") } } println(s)
  110. Ktor fun main() { embeddedServer(Netty, port = 8080, host =

    "0.0.0.0") { routing { get("/html-dsl") { call.respondHtml { body { h1 { +"HTML" } ul { for (n in 1 .. 10) { li { +"$n" } } } } } } } }.start(wait = true) }
  111. Ktor fun main() { embeddedServer(Netty, port = 8080, host =

    "0.0.0.0") { routing { get("/html-dsl") { call.respondHtml { body { h1 { +"HTML" } ul { for (n in 1 .. 10) { li { +"$n" } } } } } } } }.start(wait = true) } Ktor’s routing
  112. Ktor fun main() { embeddedServer(Netty, port = 8080, host =

    "0.0.0.0") { routing { get("/html-dsl") { call.respondHtml { body { h1 { +"HTML" } ul { for (n in 1 .. 10) { li { +"$n" } } } } } } } }.start(wait = true) } kotlinx.html Ktor’s routing
  113. Build your vocabulary to abstract from scope functions val client

    = ClientBuilder().apply { firstName = "Anton" lastName = "Arhipov" twitter = TwitterBuilder().apply { handle = "@antonarhipov" }.build() company = CompanyBuilder().apply { name = "JetBrains" city = "Tallinn" }.build() }.build() println("Created client is: $client")
  114. Build your vocabulary to abstract from scope functions fun client(c:

    ClientBuilder.() - > Unit): Client { val builder = ClientBuilder() c(builder) return builder.build() } fun ClientBuilder.company(block: CompanyBuilder.() - > Unit) { company = CompanyBuilder().apply(block).build() } fun ClientBuilder.twitter(block: TwitterBuilder.() - > Unit) { twitter = TwitterBuilder().apply(block).build() } val client = ClientBuilder().apply { firstName = "Anton" lastName = "Arhipov" twitter = TwitterBuilder().apply { handle = "@antonarhipov" }.build() company = CompanyBuilder().apply { name = "JetBrains" city = "Tallinn" }.build() }.build() println("Created client is: $client")
  115. val client = client { firstName = "Anton" lastName =

    "Arhipov" twitter { handle = "@antonarhipov" } company { name = "JetBrains" city = "Tallinn" } } println("Created client is: $client") Build your vocabulary to abstract from scope functions val client = ClientBuilder().apply { firstName = "Anton" lastName = "Arhipov" twitter = TwitterBuilder().apply { handle = "@antonarhipov" }.build() company = CompanyBuilder().apply { name = "JetBrains" city = "Tallinn" }.build() }.build() println("Created client is: $client")