SlideShare ist ein Scribd-Unternehmen logo
1 von 109
Downloaden Sie, um offline zu lesen
@antonarhipov
Idiomatic Kotlin
from formatting to DSLs
Agenda
• Expressions

• Examples from standard library

• DSL
Anton Arhipov


@antonarhipov
Developer Advocate @ JetBrains
Idiomatic - using, containing, or denoting expressions
that are natural to a native speaker
Idiomatic - using, containing, or denoting expressions
that are natural to a native speaker
In case of a programming language:

•Conforms to a commonly accepted style

•E
ff
ectively uses features of the programming language
Expressions
try, if, when
fun adjustSpeed(weather: Weather): Drive {


var result: Drive


if (weather is Rainy) {


result = Safe()


} else {


result = Calm()


}




return result


}
fun adjustSpeed(weather: Weather): Drive {


var result: Drive


if (weather is Rainy) {


result = Safe()


} else {


result = Calm()


}




return result


}
fun adjustSpeed(weather: Weather): Drive {


val result: Drive


if (weather is Rainy) {


result = Safe()


} else {


result = Calm()


}




return result


}
fun adjustSpeed(weather: Weather): Drive {


val result: Drive


if (weather is Rainy) {


result = Safe()


} else {


result = Calm()


}




return result


}
fun adjustSpeed(weather: Weather): Drive {


val result: Drive


result = if (weather is Rainy) {


Safe()


} else {


Calm()


}


return result


}
fun adjustSpeed(weather: Weather): Drive {


val result: Drive


result = if (weather is Rainy) {


Safe()


} else {


Calm()


}


return result


}
fun adjustSpeed(weather: Weather): Drive {




val result: Drive = if (weather is Rainy) {


Safe()


} else {


Calm()


}


return result


}
fun adjustSpeed(weather: Weather): Drive {




val result: Drive = if (weather is Rainy) {


Safe()


} else {


Calm()


}


return result


}
fun adjustSpeed(weather: Weather): Drive {




return if (weather is Rainy) {


Safe()


} else {


Calm()


}


}
fun adjustSpeed(weather: Weather): Drive {




return if (weather is Rainy) {


Safe()


} else {


Calm()


}


}
fun adjustSpeed(weather: Weather): Drive = if (weather is Rainy) {


Safe()


} else {


Calm()


}
fun adjustSpeed(weather: Weather): Drive = if (weather is Rainy) {


Safe()


} else {


Calm()


}
fun adjustSpeed(weather: Weather) = if (weather is Rainy) {


Safe()


} else {


Calm()


}
fun adjustSpeed(weather: Weather) = if (weather is Rainy) {


Safe()


} else {


Calm()


}
fun adjustSpeed(weather: Weather) = if (weather is Rainy) Safe() else Calm()
fun adjustSpeed(weather: Weather) = if (weather is Rainy) Safe() else Calm()


fun adjustSpeed(weather: Weather): Drive {


var result: Drive


if (weather is Rainy) {


result = Safe()


} else {


result = Calm()


}




return result


}
fun adjustSpeed(weather: Weather) = if (weather is Rainy) Safe() else Calm()
fun adjustSpeed(weather: Weather) = if (weather is Rainy) Safe() else Calm()
abstract class Weather


class Sunny : Weather()


class Rainy : Weather()


fun adjustSpeed(weather: Weather) = when (weather) {


is Rainy
-
>
Safe()


else
-
>
Calm()


}
sealed class Weather


class Sunny : Weather()


class Rainy : Weather()


fun adjustSpeed(weather: Weather) = when (weather) {


is Rainy
-
>
Safe()


/
/
else
-
>
Calm()


}
sealed class Weather


class Sunny : Weather()


class Rainy : Weather()


fun adjustSpeed(weather: Weather) = when (weather) {


is Rainy
-
>
Safe()


/
/
else
-
>
Calm()


}
sealed class Weather


class Sunny : Weather()


class Rainy : Weather()


fun adjustSpeed(weather: Weather) = when (weather) {


is Rainy
-
>
Safe()


is Sunny
-
>
TODO()


}
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
Use try as expression body
fun tryParse(number: String) : Int? {


try {


return Integer.parseInt(number)


} catch (e: NumberFormatException) {


return null


}


}
Use try as expression body
fun tryParse(number: String) = try {


Integer.parseInt(number)


} catch (e: NumberFormatException) {


null


}
Use try as expression
fun tryParse(number: String) : Int? {


val n = try {


Integer.parseInt(number)


} catch (e: NumberFormatException) {


null


}


println(n)


return n


}
Use elvis operator
class Person(val name: String?, val age: Int?)


val p = retrievePerson()
?
:
Person()
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")


}
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")


}
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")


}
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")


}
Nullability
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
Consider using null-safe call
val order = retrieveOrder()


val city = order
?
.
customer
?
.
address
?
.
city
Consider using null-safe call
val order = retrieveOrder()


val city = order
?
.
customer
?
.
address
?
.
city


?
:
throw IllegalArgumentException("Invalid Order")
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
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)


}


}
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
Consider using ?.let for null-checks
val order = retrieveOrder()


if (order
!
=
null){


processCustomer(order.customer)


}
Consider using ?.let for null-checks
val order = retrieveOrder()


if (order
!
=
null){


processCustomer(order.customer)


}
retrieveOrder()
?
.
let {


processCustomer(it.customer)


}
retrieveOrder()
?
.
customer
?
.
let {
:
:
processCustomer }
or
Consider using ?.let for null-checks
val order = retrieveOrder()


if (order
!
=
null){


processCustomer(order.customer)


}
retrieveOrder()
?
.
let {


processCustomer(it.customer)


}
No need for an extra variable
retrieveOrder()
?
.
let {


processCustomer(it.customer)


}
retrieveOrder()
?
.
customer
?
.
let {
:
:
processCustomer }
or
Consider using ?.let for null-checks
val order = retrieveOrder()


if (order
!
=
null){


processCustomer(order.customer)


}
retrieveOrder()
?
.
let {


processCustomer(it.customer)


}
retrieveOrder()
?
.
let {


processCustomer(it.customer)


}
retrieveOrder()
?
.
customer
?
.
let {
:
:
processCustomer }
or
Consider using safe cast for type checking
override fun equals(other: Any?) : Boolean {


val command = other as Command


return command.id
=
=
id


}
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


}
Use range checks instead of comparison pairs
fun isLatinUppercase(c: Char) =


c
>
=
'A'
&
&
c
<
=
'Z'
Use range checks instead of comparison pairs
fun isLatinUppercase(c: Char) =


c
>
=
'A'
&
&
c
<
=
'Z'
Use range checks instead of comparison pairs
fun isLatinUppercase(c: Char) =


c in 'A'
.
.
'Z'
Use range checks instead of comparison pairs
fun isLatinUppercase(c: Char) =


c in 'A'
.
.
'Z'
Ranges in loops
fun main(args: Array<String>) {




for (i in 0
.
.
args.size - 1) {


println("$i: ${args[i]}")


}




}
Ranges in loops
fun main(args: Array<String>) {




for (i in 0
.
.
args.size - 1) {


println("$i: ${args[i]}")


}




}
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]}")


}
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]}")


}
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")


}
Classes and Functions
Don’t create classes just to hold functions
class StringUtils {


companion object {


fun isPhoneNumber(s: String) =


s.length
=
=
7
&
&
s.all { it.isDigit() }


}


}
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() }


}
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() }
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() }
Extension or a member?
https://kotlinlang.org/docs/coding-conventions.html#extension-functions
•Use extension functions liberally.
 

•If a function works primarily on an object, consider making it an
extension with that object as a receiver.
 

•Minimize API pollution, restrict the visibility.
 

•As necessary, use local extension functions, member extension
functions, or top-level extension functions with private visibility.
Use default values instead of overloading
class Phonebook {


fun print() {


print(",")


}


fun print(columnSeparator: String) {}


}


fun main(args: Array<String>) {


Phonebook().print("|")


}
Use default values instead of overloading
class Phonebook {


fun print() {


print(",")


}


fun print(columnSeparator: String) {}


}


fun main(args: Array<String>) {


Phonebook().print("|")


}


class Phonebook {


fun print(separator: String = ",") {}


fun someFun(x: Int) {}


}


fun main(args: Array<String>) {


Phonebook().print(separator = "|")


}
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


}
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")


}
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();
Destructuring in loops
fun printMap(map: Map<String, String>) {


for (item in map.entries) {


println("${item.key}
-
>
${item.value}")


}


}
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")


}


}
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)


}
Use type aliases for functional types
class Event


class EventDispatcher {


fun addClickHandler(handler: (Event)
-
>
Unit) {}


fun removeClickHandler(handler: (Event)
-
>
Unit) {}


}
Use type aliases for functional types
class Event


class EventDispatcher {


fun addClickHandler(handler: (Event)
-
>
Unit) {}


fun removeClickHandler(handler: (Event)
-
>
Unit) {}


}
typealias ClickHandler = (Event)
-
>
Unit


class EventDispatcher {


fun addClickHandler(handler: ClickHandler) {


}


fun removeClickHandler(handler: ClickHandler) {


}


}
Standard Library
Verify parameters using require()
class Person(


val name: String?,


val age: Int


)


fun processPerson(person: Person) {


if (person.age < 18) {


throw IllegalArgumentException("Adult required")


}


}
Verify parameters using require()
class Person(


val name: String?,


val age: Int


)


fun processPerson(person: Person) {


if (person.age < 18) {


throw IllegalArgumentException("Adult required")


}


}


fun processPerson(person: Person) {


require(person.age
>
=
18) { "Adult required" }


}
Select objects by type with filterIsInstance
fun findAllStrings(objects: List<Any>) =


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


objects.filter { it is String }


fun findAllStrings(objects: List<Any>) =


objects.filterIsInstance<String>()
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>()
Apply operation to non-null elements mapNotNull
data class Result(


val data: Any?,


val error: String?


)


fun listErrors(results: List<Result>): List<String> =


results.map { it.error }.filterNotNull()


fun listErrors(results: List<Result>): List<String> =


results.mapNotNull { it.errorMessage }
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


})
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))
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)


}


}
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)


}
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)


}
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
}
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);
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")
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")
Domain Specific Languages
“Domain Speci
fi
c”,
i.e. tailored for a speci
fi
c task
“Domain Speci
fi
c”,
i.e. tailored for a speci
fi
c task
Examples:

•Compose strings - stringBuilder

•Create HTML documents - kotlinx.html

•Con
fi
gure routing logic for a web app - ktor

•Generally, build any object graphs. See “type-safe builders”
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)
kotlinx.html
System.out.appendHTML().html {


body {


div {


a("http:
/
/
kotlinlang.org") {


target = ATarget.blank


+"Main site"


}


}


}


}
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
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
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
Lambda with receiver
T.() -> Unit
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")
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")
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")
https://speakerdeck.com/antonarhipov
https://github.com/antonarhipov/idiomatic-kotlin
@antonarhipov

Weitere ähnliche Inhalte

Was ist angesagt?

Was ist angesagt? (20)

Threading Made Easy! A Busy Developer’s Guide to Kotlin Coroutines
Threading Made Easy! A Busy Developer’s Guide to Kotlin CoroutinesThreading Made Easy! A Busy Developer’s Guide to Kotlin Coroutines
Threading Made Easy! A Busy Developer’s Guide to Kotlin Coroutines
 
Java method
Java methodJava method
Java method
 
Kotlin InDepth Tutorial for beginners 2022
Kotlin InDepth Tutorial for beginners 2022Kotlin InDepth Tutorial for beginners 2022
Kotlin InDepth Tutorial for beginners 2022
 
Practical Class 12th (c++programs+sql queries and output)
Practical Class 12th (c++programs+sql queries and output) Practical Class 12th (c++programs+sql queries and output)
Practical Class 12th (c++programs+sql queries and output)
 
Data classes in kotlin by Naveed
Data classes in kotlin by NaveedData classes in kotlin by Naveed
Data classes in kotlin by Naveed
 
Java Generics Introduction - Syntax Advantages and Pitfalls
Java Generics Introduction - Syntax Advantages and PitfallsJava Generics Introduction - Syntax Advantages and Pitfalls
Java Generics Introduction - Syntax Advantages and Pitfalls
 
difference between c c++ c#
difference between c c++ c#difference between c c++ c#
difference between c c++ c#
 
[214] Ai Serving Platform: 하루 수 억 건의 인퍼런스를 처리하기 위한 고군분투기
[214] Ai Serving Platform: 하루 수 억 건의 인퍼런스를 처리하기 위한 고군분투기[214] Ai Serving Platform: 하루 수 억 건의 인퍼런스를 처리하기 위한 고군분투기
[214] Ai Serving Platform: 하루 수 억 건의 인퍼런스를 처리하기 위한 고군분투기
 
Vi Editor Cheat Sheet
Vi Editor Cheat SheetVi Editor Cheat Sheet
Vi Editor Cheat Sheet
 
Course 102: Lecture 4: Using Wild Cards
Course 102: Lecture 4: Using Wild CardsCourse 102: Lecture 4: Using Wild Cards
Course 102: Lecture 4: Using Wild Cards
 
Advance OOP concepts in Python
Advance OOP concepts in PythonAdvance OOP concepts in Python
Advance OOP concepts in Python
 
Be Smart, Constrain Your Types to Free Your Brain!
Be Smart, Constrain Your Types to Free Your Brain!Be Smart, Constrain Your Types to Free Your Brain!
Be Smart, Constrain Your Types to Free Your Brain!
 
Java Foundations: Methods
Java Foundations: MethodsJava Foundations: Methods
Java Foundations: Methods
 
Sequence and Traverse - Part 1
Sequence and Traverse - Part 1Sequence and Traverse - Part 1
Sequence and Traverse - Part 1
 
[FR] Présentatation d'Ansible
[FR] Présentatation d'Ansible [FR] Présentatation d'Ansible
[FR] Présentatation d'Ansible
 
Java Method, Static Block
Java Method, Static BlockJava Method, Static Block
Java Method, Static Block
 
Course 102: Lecture 2: Unwrapping Linux
Course 102: Lecture 2: Unwrapping Linux Course 102: Lecture 2: Unwrapping Linux
Course 102: Lecture 2: Unwrapping Linux
 
Java Docs
Java DocsJava Docs
Java Docs
 
Java interface
Java interfaceJava interface
Java interface
 
Kotlin as a Better Java
Kotlin as a Better JavaKotlin as a Better Java
Kotlin as a Better Java
 

Ähnlich wie Idiomatic kotlin

Ähnlich wie Idiomatic kotlin (20)

Scala vs Java 8 in a Java 8 World
Scala vs Java 8 in a Java 8 WorldScala vs Java 8 in a Java 8 World
Scala vs Java 8 in a Java 8 World
 
Exploring Koltin on Android
Exploring Koltin on AndroidExploring Koltin on Android
Exploring Koltin on Android
 
Kotlin Basics - Apalon Kotlin Sprint Part 2
Kotlin Basics - Apalon Kotlin Sprint Part 2Kotlin Basics - Apalon Kotlin Sprint Part 2
Kotlin Basics - Apalon Kotlin Sprint Part 2
 
Swift - 혼자 공부하면 분명히 안할테니까 같이 공부하기
Swift - 혼자 공부하면 분명히 안할테니까 같이 공부하기Swift - 혼자 공부하면 분명히 안할테니까 같이 공부하기
Swift - 혼자 공부하면 분명히 안할테니까 같이 공부하기
 
Kotlin Advanced - Apalon Kotlin Sprint Part 3
Kotlin Advanced - Apalon Kotlin Sprint Part 3Kotlin Advanced - Apalon Kotlin Sprint Part 3
Kotlin Advanced - Apalon Kotlin Sprint Part 3
 
Concurrent Application Development using Scala
Concurrent Application Development using ScalaConcurrent Application Development using Scala
Concurrent Application Development using Scala
 
Derping With Kotlin
Derping With KotlinDerping With Kotlin
Derping With Kotlin
 
Kotlinify Your Project!
Kotlinify Your Project!Kotlinify Your Project!
Kotlinify Your Project!
 
SDC - Einführung in Scala
SDC - Einführung in ScalaSDC - Einführung in Scala
SDC - Einführung in Scala
 
Introduction to Swift programming language.
Introduction to Swift programming language.Introduction to Swift programming language.
Introduction to Swift programming language.
 
Introduction to Scala
Introduction to ScalaIntroduction to Scala
Introduction to Scala
 
Parametricity - #cljsyd - May, 2015
Parametricity - #cljsyd - May, 2015Parametricity - #cljsyd - May, 2015
Parametricity - #cljsyd - May, 2015
 
Pooya Khaloo Presentation on IWMC 2015
Pooya Khaloo Presentation on IWMC 2015Pooya Khaloo Presentation on IWMC 2015
Pooya Khaloo Presentation on IWMC 2015
 
A swift introduction to Swift
A swift introduction to SwiftA swift introduction to Swift
A swift introduction to Swift
 
Ast transformations
Ast transformationsAst transformations
Ast transformations
 
Dip into Coroutines - KTUG Munich 202303
Dip into Coroutines - KTUG Munich 202303Dip into Coroutines - KTUG Munich 202303
Dip into Coroutines - KTUG Munich 202303
 
Swift-Programming Part 1
Swift-Programming Part 1Swift-Programming Part 1
Swift-Programming Part 1
 
Why Scala is the better Java
Why Scala is the better JavaWhy Scala is the better Java
Why Scala is the better Java
 
Functional Programming with Groovy
Functional Programming with GroovyFunctional Programming with Groovy
Functional Programming with Groovy
 
Kotlin
KotlinKotlin
Kotlin
 

Mehr von Anton Arhipov

JavaZone 2022 - Building Kotlin DSL.pdf
JavaZone 2022 - Building Kotlin DSL.pdfJavaZone 2022 - Building Kotlin DSL.pdf
JavaZone 2022 - Building Kotlin DSL.pdf
Anton Arhipov
 
Riga DevDays 2017 - The hitchhiker’s guide to Java class reloading
Riga DevDays 2017 - The hitchhiker’s guide to Java class reloadingRiga DevDays 2017 - The hitchhiker’s guide to Java class reloading
Riga DevDays 2017 - The hitchhiker’s guide to Java class reloading
Anton Arhipov
 
Devclub 01/2017 - (Не)адекватное Java-интервью
Devclub 01/2017 - (Не)адекватное Java-интервьюDevclub 01/2017 - (Не)адекватное Java-интервью
Devclub 01/2017 - (Не)адекватное Java-интервью
Anton Arhipov
 

Mehr von Anton Arhipov (20)

JavaZone 2022 - Building Kotlin DSL.pdf
JavaZone 2022 - Building Kotlin DSL.pdfJavaZone 2022 - Building Kotlin DSL.pdf
JavaZone 2022 - Building Kotlin DSL.pdf
 
TechTrain 2019 - (Не)адекватное техническое интервью
TechTrain 2019 - (Не)адекватное техническое интервьюTechTrain 2019 - (Не)адекватное техническое интервью
TechTrain 2019 - (Не)адекватное техническое интервью
 
Build pipelines with TeamCity
Build pipelines with TeamCityBuild pipelines with TeamCity
Build pipelines with TeamCity
 
Build pipelines with TeamCity
Build pipelines with TeamCityBuild pipelines with TeamCity
Build pipelines with TeamCity
 
Devoxx Ukraine 2018 - Kotlin DSL in under an hour
Devoxx Ukraine 2018 - Kotlin DSL in under an hourDevoxx Ukraine 2018 - Kotlin DSL in under an hour
Devoxx Ukraine 2018 - Kotlin DSL in under an hour
 
GeeCON Prague 2018 - Kotlin DSL in under an hour
GeeCON Prague 2018 - Kotlin DSL in under an hourGeeCON Prague 2018 - Kotlin DSL in under an hour
GeeCON Prague 2018 - Kotlin DSL in under an hour
 
Build pipelines with TeamCity and Kotlin DSL
Build pipelines with TeamCity and Kotlin DSLBuild pipelines with TeamCity and Kotlin DSL
Build pipelines with TeamCity and Kotlin DSL
 
Build pipelines with TeamCity
Build pipelines with TeamCityBuild pipelines with TeamCity
Build pipelines with TeamCity
 
JavaDay Kiev 2017 - Integration testing with TestContainers
JavaDay Kiev 2017 - Integration testing with TestContainersJavaDay Kiev 2017 - Integration testing with TestContainers
JavaDay Kiev 2017 - Integration testing with TestContainers
 
GeeCON Prague 2017 - TestContainers
GeeCON Prague 2017 - TestContainersGeeCON Prague 2017 - TestContainers
GeeCON Prague 2017 - TestContainers
 
JavaOne 2017 - The hitchhiker’s guide to Java class reloading
JavaOne 2017 - The hitchhiker’s guide to Java class reloadingJavaOne 2017 - The hitchhiker’s guide to Java class reloading
JavaOne 2017 - The hitchhiker’s guide to Java class reloading
 
JavaOne 2017 - TestContainers: integration testing without the hassle
JavaOne 2017 - TestContainers: integration testing without the hassleJavaOne 2017 - TestContainers: integration testing without the hassle
JavaOne 2017 - TestContainers: integration testing without the hassle
 
JavaOne 2017 - The hitchhiker’s guide to Java class reloading
JavaOne 2017 - The hitchhiker’s guide to Java class reloadingJavaOne 2017 - The hitchhiker’s guide to Java class reloading
JavaOne 2017 - The hitchhiker’s guide to Java class reloading
 
JavaZone 2017 - The Hitchhiker’s guide to Java class reloading
JavaZone 2017 - The Hitchhiker’s guide to Java class reloadingJavaZone 2017 - The Hitchhiker’s guide to Java class reloading
JavaZone 2017 - The Hitchhiker’s guide to Java class reloading
 
JUG.ua 20170225 - Java bytecode instrumentation
JUG.ua 20170225 - Java bytecode instrumentationJUG.ua 20170225 - Java bytecode instrumentation
JUG.ua 20170225 - Java bytecode instrumentation
 
Riga DevDays 2017 - The hitchhiker’s guide to Java class reloading
Riga DevDays 2017 - The hitchhiker’s guide to Java class reloadingRiga DevDays 2017 - The hitchhiker’s guide to Java class reloading
Riga DevDays 2017 - The hitchhiker’s guide to Java class reloading
 
GeeCON 2017 - TestContainers. Integration testing without the hassle
GeeCON 2017 - TestContainers. Integration testing without the hassleGeeCON 2017 - TestContainers. Integration testing without the hassle
GeeCON 2017 - TestContainers. Integration testing without the hassle
 
JEEConf 2017 - The hitchhiker’s guide to Java class reloading
JEEConf 2017 - The hitchhiker’s guide to Java class reloadingJEEConf 2017 - The hitchhiker’s guide to Java class reloading
JEEConf 2017 - The hitchhiker’s guide to Java class reloading
 
JEEConf 2017 - Having fun with Javassist
JEEConf 2017 - Having fun with JavassistJEEConf 2017 - Having fun with Javassist
JEEConf 2017 - Having fun with Javassist
 
Devclub 01/2017 - (Не)адекватное Java-интервью
Devclub 01/2017 - (Не)адекватное Java-интервьюDevclub 01/2017 - (Не)адекватное Java-интервью
Devclub 01/2017 - (Не)адекватное Java-интервью
 

Kürzlich hochgeladen

Future Visions: Predictions to Guide and Time Tech Innovation, Peter Udo Diehl
Future Visions: Predictions to Guide and Time Tech Innovation, Peter Udo DiehlFuture Visions: Predictions to Guide and Time Tech Innovation, Peter Udo Diehl
Future Visions: Predictions to Guide and Time Tech Innovation, Peter Udo Diehl
Peter Udo Diehl
 

Kürzlich hochgeladen (20)

PLAI - Acceleration Program for Generative A.I. Startups
PLAI - Acceleration Program for Generative A.I. StartupsPLAI - Acceleration Program for Generative A.I. Startups
PLAI - Acceleration Program for Generative A.I. Startups
 
Choosing the Right FDO Deployment Model for Your Application _ Geoffrey at In...
Choosing the Right FDO Deployment Model for Your Application _ Geoffrey at In...Choosing the Right FDO Deployment Model for Your Application _ Geoffrey at In...
Choosing the Right FDO Deployment Model for Your Application _ Geoffrey at In...
 
Free and Effective: Making Flows Publicly Accessible, Yumi Ibrahimzade
Free and Effective: Making Flows Publicly Accessible, Yumi IbrahimzadeFree and Effective: Making Flows Publicly Accessible, Yumi Ibrahimzade
Free and Effective: Making Flows Publicly Accessible, Yumi Ibrahimzade
 
How we scaled to 80K users by doing nothing!.pdf
How we scaled to 80K users by doing nothing!.pdfHow we scaled to 80K users by doing nothing!.pdf
How we scaled to 80K users by doing nothing!.pdf
 
Future Visions: Predictions to Guide and Time Tech Innovation, Peter Udo Diehl
Future Visions: Predictions to Guide and Time Tech Innovation, Peter Udo DiehlFuture Visions: Predictions to Guide and Time Tech Innovation, Peter Udo Diehl
Future Visions: Predictions to Guide and Time Tech Innovation, Peter Udo Diehl
 
Optimizing NoSQL Performance Through Observability
Optimizing NoSQL Performance Through ObservabilityOptimizing NoSQL Performance Through Observability
Optimizing NoSQL Performance Through Observability
 
Salesforce Adoption – Metrics, Methods, and Motivation, Antone Kom
Salesforce Adoption – Metrics, Methods, and Motivation, Antone KomSalesforce Adoption – Metrics, Methods, and Motivation, Antone Kom
Salesforce Adoption – Metrics, Methods, and Motivation, Antone Kom
 
Portal Kombat : extension du réseau de propagande russe
Portal Kombat : extension du réseau de propagande russePortal Kombat : extension du réseau de propagande russe
Portal Kombat : extension du réseau de propagande russe
 
How Red Hat Uses FDO in Device Lifecycle _ Costin and Vitaliy at Red Hat.pdf
How Red Hat Uses FDO in Device Lifecycle _ Costin and Vitaliy at Red Hat.pdfHow Red Hat Uses FDO in Device Lifecycle _ Costin and Vitaliy at Red Hat.pdf
How Red Hat Uses FDO in Device Lifecycle _ Costin and Vitaliy at Red Hat.pdf
 
AI presentation and introduction - Retrieval Augmented Generation RAG 101
AI presentation and introduction - Retrieval Augmented Generation RAG 101AI presentation and introduction - Retrieval Augmented Generation RAG 101
AI presentation and introduction - Retrieval Augmented Generation RAG 101
 
Linux Foundation Edge _ Overview of FDO Software Components _ Randy at Intel.pdf
Linux Foundation Edge _ Overview of FDO Software Components _ Randy at Intel.pdfLinux Foundation Edge _ Overview of FDO Software Components _ Randy at Intel.pdf
Linux Foundation Edge _ Overview of FDO Software Components _ Randy at Intel.pdf
 
1111 ChatGPT Prompts PDF Free Download - Prompts for ChatGPT
1111 ChatGPT Prompts PDF Free Download - Prompts for ChatGPT1111 ChatGPT Prompts PDF Free Download - Prompts for ChatGPT
1111 ChatGPT Prompts PDF Free Download - Prompts for ChatGPT
 
Secure Zero Touch enabled Edge compute with Dell NativeEdge via FDO _ Brad at...
Secure Zero Touch enabled Edge compute with Dell NativeEdge via FDO _ Brad at...Secure Zero Touch enabled Edge compute with Dell NativeEdge via FDO _ Brad at...
Secure Zero Touch enabled Edge compute with Dell NativeEdge via FDO _ Brad at...
 
IESVE for Early Stage Design and Planning
IESVE for Early Stage Design and PlanningIESVE for Early Stage Design and Planning
IESVE for Early Stage Design and Planning
 
Oauth 2.0 Introduction and Flows with MuleSoft
Oauth 2.0 Introduction and Flows with MuleSoftOauth 2.0 Introduction and Flows with MuleSoft
Oauth 2.0 Introduction and Flows with MuleSoft
 
Simplified FDO Manufacturing Flow with TPMs _ Liam at Infineon.pdf
Simplified FDO Manufacturing Flow with TPMs _ Liam at Infineon.pdfSimplified FDO Manufacturing Flow with TPMs _ Liam at Infineon.pdf
Simplified FDO Manufacturing Flow with TPMs _ Liam at Infineon.pdf
 
FDO for Camera, Sensor and Networking Device – Commercial Solutions from VinC...
FDO for Camera, Sensor and Networking Device – Commercial Solutions from VinC...FDO for Camera, Sensor and Networking Device – Commercial Solutions from VinC...
FDO for Camera, Sensor and Networking Device – Commercial Solutions from VinC...
 
Introduction to FDO and How It works Applications _ Richard at FIDO Alliance.pdf
Introduction to FDO and How It works Applications _ Richard at FIDO Alliance.pdfIntroduction to FDO and How It works Applications _ Richard at FIDO Alliance.pdf
Introduction to FDO and How It works Applications _ Richard at FIDO Alliance.pdf
 
Designing for Hardware Accessibility at Comcast
Designing for Hardware Accessibility at ComcastDesigning for Hardware Accessibility at Comcast
Designing for Hardware Accessibility at Comcast
 
WebAssembly is Key to Better LLM Performance
WebAssembly is Key to Better LLM PerformanceWebAssembly is Key to Better LLM Performance
WebAssembly is Key to Better LLM Performance
 

Idiomatic kotlin

  • 2. Agenda • Expressions • Examples from standard library • DSL
  • 4.
  • 5.
  • 6.
  • 7. Idiomatic - using, containing, or denoting expressions that are natural to a native speaker
  • 8. Idiomatic - using, containing, or denoting expressions that are natural to a native speaker In case of a programming language: •Conforms to a commonly accepted style •E ff ectively uses features of the programming language
  • 9.
  • 11. fun adjustSpeed(weather: Weather): Drive { var result: Drive if (weather is Rainy) { result = Safe() } else { result = Calm() } return result }
  • 12. fun adjustSpeed(weather: Weather): Drive { var result: Drive if (weather is Rainy) { result = Safe() } else { result = Calm() } return result }
  • 13. fun adjustSpeed(weather: Weather): Drive { val result: Drive if (weather is Rainy) { result = Safe() } else { result = Calm() } return result }
  • 14. fun adjustSpeed(weather: Weather): Drive { val result: Drive if (weather is Rainy) { result = Safe() } else { result = Calm() } return result }
  • 15. fun adjustSpeed(weather: Weather): Drive { val result: Drive result = if (weather is Rainy) { Safe() } else { Calm() } return result }
  • 16. fun adjustSpeed(weather: Weather): Drive { val result: Drive result = if (weather is Rainy) { Safe() } else { Calm() } return result }
  • 17. fun adjustSpeed(weather: Weather): Drive { val result: Drive = if (weather is Rainy) { Safe() } else { Calm() } return result }
  • 18. fun adjustSpeed(weather: Weather): Drive { val result: Drive = if (weather is Rainy) { Safe() } else { Calm() } return result }
  • 19. fun adjustSpeed(weather: Weather): Drive { return if (weather is Rainy) { Safe() } else { Calm() } }
  • 20. fun adjustSpeed(weather: Weather): Drive { return if (weather is Rainy) { Safe() } else { Calm() } }
  • 21. fun adjustSpeed(weather: Weather): Drive = if (weather is Rainy) { Safe() } else { Calm() }
  • 22. fun adjustSpeed(weather: Weather): Drive = if (weather is Rainy) { Safe() } else { Calm() }
  • 23. fun adjustSpeed(weather: Weather) = if (weather is Rainy) { Safe() } else { Calm() }
  • 24. fun adjustSpeed(weather: Weather) = if (weather is Rainy) { Safe() } else { Calm() }
  • 25. fun adjustSpeed(weather: Weather) = if (weather is Rainy) Safe() else Calm()
  • 26. fun adjustSpeed(weather: Weather) = if (weather is Rainy) Safe() else Calm() fun adjustSpeed(weather: Weather): Drive { var result: Drive if (weather is Rainy) { result = Safe() } else { result = Calm() } return result }
  • 27. fun adjustSpeed(weather: Weather) = if (weather is Rainy) Safe() else Calm()
  • 28. fun adjustSpeed(weather: Weather) = if (weather is Rainy) Safe() else Calm()
  • 29. abstract class Weather class Sunny : Weather() class Rainy : Weather() fun adjustSpeed(weather: Weather) = when (weather) { is Rainy - > Safe() else - > Calm() }
  • 30. sealed class Weather class Sunny : Weather() class Rainy : Weather() fun adjustSpeed(weather: Weather) = when (weather) { is Rainy - > Safe() / / else - > Calm() }
  • 31. sealed class Weather class Sunny : Weather() class Rainy : Weather() fun adjustSpeed(weather: Weather) = when (weather) { is Rainy - > Safe() / / else - > Calm() }
  • 32. sealed class Weather class Sunny : Weather() class Rainy : Weather() fun adjustSpeed(weather: Weather) = when (weather) { is Rainy - > Safe() is Sunny - > TODO() }
  • 33. 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
  • 34. Use try as expression body fun tryParse(number: String) : Int? { try { return Integer.parseInt(number) } catch (e: NumberFormatException) { return null } }
  • 35. Use try as expression body fun tryParse(number: String) = try { Integer.parseInt(number) } catch (e: NumberFormatException) { null }
  • 36. Use try as expression fun tryParse(number: String) : Int? { val n = try { Integer.parseInt(number) } catch (e: NumberFormatException) { null } println(n) return n }
  • 37. Use elvis operator class Person(val name: String?, val age: Int?) val p = retrievePerson() ? : Person()
  • 38. 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") }
  • 39. 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") }
  • 40. 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") }
  • 41. 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") }
  • 43. 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
  • 44. Consider using null-safe call val order = retrieveOrder() val city = order ? . customer ? . address ? . city
  • 45. Consider using null-safe call val order = retrieveOrder() val city = order ? . customer ? . address ? . city ? : throw IllegalArgumentException("Invalid Order")
  • 46. 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
  • 47. 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) } }
  • 48. 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
  • 49. Consider using ?.let for null-checks val order = retrieveOrder() if (order ! = null){ processCustomer(order.customer) }
  • 50. Consider using ?.let for null-checks val order = retrieveOrder() if (order ! = null){ processCustomer(order.customer) } retrieveOrder() ? . let { processCustomer(it.customer) } retrieveOrder() ? . customer ? . let { : : processCustomer } or
  • 51. Consider using ?.let for null-checks val order = retrieveOrder() if (order ! = null){ processCustomer(order.customer) } retrieveOrder() ? . let { processCustomer(it.customer) } No need for an extra variable retrieveOrder() ? . let { processCustomer(it.customer) } retrieveOrder() ? . customer ? . let { : : processCustomer } or
  • 52. Consider using ?.let for null-checks val order = retrieveOrder() if (order ! = null){ processCustomer(order.customer) } retrieveOrder() ? . let { processCustomer(it.customer) } retrieveOrder() ? . let { processCustomer(it.customer) } retrieveOrder() ? . customer ? . let { : : processCustomer } or
  • 53. Consider using safe cast for type checking override fun equals(other: Any?) : Boolean { val command = other as Command return command.id = = id }
  • 54. 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 }
  • 55. Use range checks instead of comparison pairs fun isLatinUppercase(c: Char) = c > = 'A' & & c < = 'Z'
  • 56. Use range checks instead of comparison pairs fun isLatinUppercase(c: Char) = c > = 'A' & & c < = 'Z'
  • 57. Use range checks instead of comparison pairs fun isLatinUppercase(c: Char) = c in 'A' . . 'Z'
  • 58. Use range checks instead of comparison pairs fun isLatinUppercase(c: Char) = c in 'A' . . 'Z'
  • 59. Ranges in loops fun main(args: Array<String>) { for (i in 0 . . args.size - 1) { println("$i: ${args[i]}") } }
  • 60. Ranges in loops fun main(args: Array<String>) { for (i in 0 . . args.size - 1) { println("$i: ${args[i]}") } }
  • 61. 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]}") }
  • 62. 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]}") }
  • 63. 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") }
  • 65. Don’t create classes just to hold functions class StringUtils { companion object { fun isPhoneNumber(s: String) = s.length = = 7 & & s.all { it.isDigit() } } }
  • 66. 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() } }
  • 67. 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() }
  • 68. 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() }
  • 69. Extension or a member? https://kotlinlang.org/docs/coding-conventions.html#extension-functions •Use extension functions liberally. •If a function works primarily on an object, consider making it an extension with that object as a receiver. •Minimize API pollution, restrict the visibility. •As necessary, use local extension functions, member extension functions, or top-level extension functions with private visibility.
  • 70. Use default values instead of overloading class Phonebook { fun print() { print(",") } fun print(columnSeparator: String) {} } fun main(args: Array<String>) { Phonebook().print("|") }
  • 71. Use default values instead of overloading class Phonebook { fun print() { print(",") } fun print(columnSeparator: String) {} } fun main(args: Array<String>) { Phonebook().print("|") } class Phonebook { fun print(separator: String = ",") {} fun someFun(x: Int) {} } fun main(args: Array<String>) { Phonebook().print(separator = "|") }
  • 72. 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 }
  • 73. 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") }
  • 74. 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();
  • 75. Destructuring in loops fun printMap(map: Map<String, String>) { for (item in map.entries) { println("${item.key} - > ${item.value}") } }
  • 76. 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") } }
  • 77. 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) }
  • 78.
  • 79. Use type aliases for functional types class Event class EventDispatcher { fun addClickHandler(handler: (Event) - > Unit) {} fun removeClickHandler(handler: (Event) - > Unit) {} }
  • 80. Use type aliases for functional types class Event class EventDispatcher { fun addClickHandler(handler: (Event) - > Unit) {} fun removeClickHandler(handler: (Event) - > Unit) {} } typealias ClickHandler = (Event) - > Unit class EventDispatcher { fun addClickHandler(handler: ClickHandler) { } fun removeClickHandler(handler: ClickHandler) { } }
  • 82. Verify parameters using require() class Person( val name: String?, val age: Int ) fun processPerson(person: Person) { if (person.age < 18) { throw IllegalArgumentException("Adult required") } }
  • 83. Verify parameters using require() class Person( val name: String?, val age: Int ) fun processPerson(person: Person) { if (person.age < 18) { throw IllegalArgumentException("Adult required") } } fun processPerson(person: Person) { require(person.age > = 18) { "Adult required" } }
  • 84. Select objects by type with filterIsInstance fun findAllStrings(objects: List<Any>) = objects.filter { it is String }
  • 85. Select objects by type with filterIsInstance fun findAllStrings(objects: List<Any>) = objects.filter { it is String } fun findAllStrings(objects: List<Any>) = objects.filterIsInstance<String>()
  • 86. 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>()
  • 87. Apply operation to non-null elements mapNotNull data class Result( val data: Any?, val error: String? ) fun listErrors(results: List<Result>): List<String> = results.map { it.error }.filterNotNull() fun listErrors(results: List<Result>): List<String> = results.mapNotNull { it.errorMessage }
  • 88. 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 })
  • 89. 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))
  • 90. 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) } }
  • 91. 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) }
  • 92. 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) }
  • 93. 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 }
  • 94. 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);
  • 95. 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")
  • 96. 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")
  • 98. “Domain Speci fi c”, i.e. tailored for a speci fi c task
  • 99. “Domain Speci fi c”, i.e. tailored for a speci fi c task Examples: •Compose strings - stringBuilder •Create HTML documents - kotlinx.html •Con fi gure routing logic for a web app - ktor •Generally, build any object graphs. See “type-safe builders”
  • 100. 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)
  • 101. kotlinx.html System.out.appendHTML().html { body { div { a("http: / / kotlinlang.org") { target = ATarget.blank +"Main site" } } } }
  • 102. 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) }
  • 103. 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
  • 104. 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
  • 106. 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")
  • 107. 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")
  • 108. 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")