kotlin: maybe it's the right time

67
Kotlin: maybe it’s the right time Davide Cerbo - Entaksi CODEMOTION MILAN - SPECIAL EDITION 10 – 11 NOVEMBER 2017

Upload: davide-cerbo

Post on 21-Jan-2018

215 views

Category:

Software


1 download

TRANSCRIPT

Page 1: Kotlin: maybe it's the right time

Kotlin: maybe it’s the right timeDavide Cerbo - Entaksi

CODEMOTION MILAN - SPECIAL EDITION 10 – 11 NOVEMBER 2017

Page 2: Kotlin: maybe it's the right time

Chi sono

Hi, my name is Davide and I’m writing bad code since I was 7. Since 2003 I’m paid for that.

Page 3: Kotlin: maybe it's the right time

DisclaimerHere we have a lot of println, and some jokes.

My wife said that they aren’t funny.

Page 4: Kotlin: maybe it's the right time

C’era una volta OAK

It was 1992 and in Italy there was a big scandal: Tangentopoli.

Page 5: Kotlin: maybe it's the right time

C’era una volta OAK Java

Borns in 1995 and now can drink beers.

Page 6: Kotlin: maybe it's the right time

Il grande problema

Backward compatibility

(The art of killing your app because the environment evolve while you can’t)

JDK 1.0 (21 january 1996)JDK 1.1 (19 february 1997)

J2SE 1.2 (8 december 1998)J2SE 1.3 (8 my 2000)

J2SE 1.4 (6 february 2002)J2SE 5.0 (30 september 2004)Java SE 6 (11 december 2006)

Java SE 7 (28 july 2011)Java SE 8 (18 march 2014)

Java SE 9 (26 september 2017)

Tutte compatibili!

Page 7: Kotlin: maybe it's the right time

JVM != Java

JAVA JVM

Page 8: Kotlin: maybe it's the right time

JVM != Java

class Hello {

fun sayHello(): String {

return "hello!"

}

}

public final class Hello {

// access flags 0x11 public final sayHello()Ljava/lang/String; @Lorg/jetbrains/annotations/NotNull;() // invisible L0 LINENUMBER 8 L0 LDC "hello!" ARETURN L1 LOCALVARIABLE this Ltotest/Hello; L0 L1 0 MAXSTACK = 1 MAXLOCALS = 1

// access flags 0x1 public <init>()V L0 LINENUMBER 6 L0 ALOAD 0 INVOKESPECIAL java/lang/Object.<init> ()V RETURN L1 LOCALVARIABLE this L/Hello; L0 L1 0 MAXSTACK = 1 MAXLOCALS = 1

@Lkotlin/Metadata;(mv={1, 1, 1}, bv={1, 0, 2}, k=1, d1={"\u0000\u0012\n\u0002\u0018\u0002\n\u0002\u0010\u0000\n\u0002\u0008\u0002\n\u0002\u0010\u000e\n\u0000\u0018\u00002\u00020\u0001B\u0005\u00a2\u0006\u0002\u0010\u0002J\u0006\u0010\u0003\u001a\u00020\u0004\u00a8\u0006\u0005", d2={"L/Hello;", "", "()V", "sayHello", "", "production sources for module coroutine_main"}) // compiled from: Hello.kt}

@Metadata(

mv = {1, 1, 7},

bv = {1, 0, 2},

k = 1,

d1 =

{"\u0000\u0012\n\u0002\u0018\u0002\n\u0002\u0010\u0000\n\u0002\b\u0002\n\u0002\u0010\u000e\n\u0000\u0018\u00002\u00020\u0001B\

u0005¢\u0006\u0002\u0010\u0002J\u0006\u0010\u0003\u001a\u00020\u0004¨\u0006\u0005"},

d2 = {"Ltotest/Hello;", "", "()V", "sayHello", "", "production sources for module coroutine_main"}

)

public final class Hello {

@NotNull

public final String sayHello() {

return "hello!";

}

}

Compile Decompile

Page 9: Kotlin: maybe it's the right time

Machine code is not enought?

Assembly languagesMachine code

Logic languages(Prolog)

Functional languages(Haskell)

Procedural languages(C)

Object-oriented languages(Java)

The Case for Kotlin and Ceylon by Russel Winderhttps://www.youtube.com/watch?v=cFL_DDXBkJQ

Page 10: Kotlin: maybe it's the right time

Why Kotlin?

Hey! People said the same about Scala in the past. So why not Scala?

Page 11: Kotlin: maybe it's the right time

So, why not Scala?

Kotlin is a better Java while Scala is more powerful than Java, and probably than Kotlin. But, Kotlin borns in the industry for the industry

and it evolves with the industry in mind, while Scala borns at the university and it is adapted to the industry. Unfortunately, we work

for the industry.

https://agilewombat.com/2016/02/01/scala-vs-kotlin/ https://superkotlin.com/kotlin-vs-scala/

Page 12: Kotlin: maybe it's the right time
Page 13: Kotlin: maybe it's the right time

Kotlin is a statically-typed programming language that runs on the Java Virtual Machine and also can be compiled to JavaScript source code or uses the LLVM compiler infrastructure.

Page 14: Kotlin: maybe it's the right time

https://dzone.com/articles/why-learn-kotlin-infographic

Since 2010(2012): KOTLIN!

Page 15: Kotlin: maybe it's the right time

04 Gennaio 2017Introducing Kotlin support in Spring Framework 5.0

https://spring.io/blog/2017/01/04/introducing-kotlin-support-in-spring-framework-5-0https://github.com/sdeleuze/spring-kotlin-deepdive

Page 16: Kotlin: maybe it's the right time

17 Maggio 2017Kotlin on Android. Now official

https://blog.jetbrains.com/kotlin/2017/05/kotlin-on-android-now-official/

http://nilhcem.com/swift-is-like-kotlin/

Page 17: Kotlin: maybe it's the right time

5 Giugno 2017kotlin-react borns!

https://github.com/JetBrains/kotlin-wrappers/commits/master/kotlin-react

https://github.com/JetBrains/create-react-kotlin-app

Page 18: Kotlin: maybe it's the right time

1.0 Release

1.1 Release

Kotlin is born

Gradle

Android Official

Spring

Statische download

Page 19: Kotlin: maybe it's the right time

Please shutdown the lights

Page 20: Kotlin: maybe it's the right time

Var o Val

var serie = "B"

var a = "Salernitana will win Serie $serie"

val b = "Salernitana will win Serie $serie"

Page 21: Kotlin: maybe it's the right time

Fun

fun main(args: Array<String>){

hello("Davide", "Salerno")

}

fun hello(name: String, city: String){

println("Hello $name from $city")

}

Page 22: Kotlin: maybe it's the right time

Fun fun functions

fun hello(name: String, city: String = "Salerno") = println("Hello $name from $city")

hello("Davide", "Salerno")

hello(name = "Davide")

hello(city = "Salerno", name = "Valentina")

fun Int.multilpy(x: Int): Int = this * x // 5.multilpy(10)

infix fun Int.multilpy(x: Int): Int = this * x // 5 multilpy 10

Aiuta anche nel refactoring!

Page 23: Kotlin: maybe it's the right time

E gli operatori?data class Point(val x: Int, val y: Int) {

operator fun plus(a: Point) = Point(x + a.x, y + a.y)

}

operator fun Point.unaryMinus() = Point(-x, -y)

val point = Point(10, 20)

println(point + point + point) //Point(x=30, y=60)

println(-point) // Point(x=-10, y=-20)

Page 24: Kotlin: maybe it's the right time

Tailrec● Rercursion, in some language, can cause: StackOverflow!

● Reduce stack execution.

● Tailrec will resolve this issue only if the recursive call is the last one.

● How? Transform your code in imperative code. Less readable, but more fast.

● Performance improvement

fun factorial(n: Long): Long = if (n <= 1L) n else n * factorial(n - 1)

tailrec fun factorial(n: Long, accumulator: Long = 1): Long = if (n <= 1L) accumulator

else factorial(n - 1, accumulator * n)

Tailrec cannot

applicable

“If” will return the verified condition value

Page 25: Kotlin: maybe it's the right time

Tailrec decompiledpublic static final long factorial(long n, long accumulator) { while(n > 1L) { long var10000 = n - 1L; accumulator *= n; n = var10000; } return accumulator; }

public static long factorial$default(long var0, long var2, int var4, Object var5) { if((var4 & 2) != 0) { var2 = 1L; } return factorial(var0, var2); }}

public static final long factorial(long n, long accumulator) { return n <= 1L?accumulator:factorial(n - 1L, accumulator * n);}

public static long factorial$default(long var0, long var2, int var4, Object var5) { if((var4 & 2) != 0) { var2 = 1L; }

return factorial(var0, var2);}

With

TailrecWithout

TailrecStackOverflow

Tools > Kotlin > Show Kotlin Bytecode > Decompile

Page 26: Kotlin: maybe it's the right time

Class

open class Person(val name: String) {

init { println("init…") }

open fun speak() { println("Hi $name!") }

infix fun and(o: Person) = "Hi ${o.name} & ${this.name}"

}

fun main(args: Array<String>) {

Person("Davide") and Person("Valentina")

val p = Person("Jack")

p.speak()

}

Page 27: Kotlin: maybe it's the right time

Class

class Customer(name: String) : Person(name) {

override fun speak() {

println("Welcome $name!")

}

}

class CustomerDavide : Person("Davide") {

override fun speak() {

println("Welcome $name!")

}

}

Page 28: Kotlin: maybe it's the right time

Equals, hashCode, toString e copy, nevermore!

data class User(val name: String, val age: Int)

val davide = User("Davide", 35)

val davideJunior = davide.copy(age=0)

fun main(args: Array<String>) {

val (name, age) = davide

println("$name $age years old")

}

deconstructing

Page 29: Kotlin: maybe it's the right time

Nothing is equal as appear

data class Point(val x: Int, val y: Int)

val a = Point(1, 2)

val b = Point(1, 2)

val c = a

println(a === b) // false

println(a == b) // true

println(a === c) // true

println(a == c) // true

Controlla il riferimento

.equals(...)

Page 30: Kotlin: maybe it's the right time

Lambda

fun main(args: Array<String>) {

arrayOf("Valentina", "Davide").forEach { println("Hello $it!") }

val ints = arrayOf(1, 2, 3)

val logger = { msg: Any -> println("log $msg") }

ints.map { value -> value * 2 }.map { v -> logger(v) }

ints.map { it * 2 }.map { logger(it) }

}

Page 31: Kotlin: maybe it's the right time

Lambda

class Customer(val name: String) {

fun forEach(action: (char: Char) -> Unit) = name.forEach(action)

fun hello(callback: (name: String) -> Unit) = callback(name)

fun upperCaseAndConcat(callback: () -> String) = "${callback()} $name".toUpperCase()

}

fun main(args: Array<String>) {

val customer = Customer("Davide")

customer.hello { println("Ciao $it") }

println(customer.upperCaseAndConcat { "Cerbo" })

customer.forEach { println(it) }

}

È il tipo con un solo valore vuoto. Corrisponde a void in Java.

Page 32: Kotlin: maybe it's the right time

Lambda & Function Label

fun forEachTest() {

val numbers = 1..100

numbers.forEach {

if (it == 25) {

return

}

println("index $it")

}

println("Hello")

}

fun forEachTest() {

val numbers = 1..100

numbers.forEach {

if (it == 25) {

return@forEach

}

println("index $it")

}

println("Hello")

}

Vs.

Page 33: Kotlin: maybe it's the right time

Tell me when, when?

fun describe(obj: Any): String = when (obj) {

1 -> "One"

"Hello" -> "Greeting"

is Long -> "Long"

!is String -> "Not a string"

else -> "Unknown"

}

describe(Person("davide"))

Page 34: Kotlin: maybe it's the right time

Null is safe!var testA:String = "ciao"

var testB:String? = "ciao"

testB = null

val nullableList: List<Int?> =

listOf(1, 2, null, 4)

val intList: List<Int> =

nullableList.filterNotNull()

println("a0 ${testA.length}")

println("a1 ${testA?.length}")

println("a2 ${testA!!.length}")

println("b0 ${testB.length}")

// ^ Not safe! Compile time error! ^

println("b1 ${testB?.length}")

println("b2 ${testB!!.length}")

// ^ KotlinNullPointerException ^

// NPE Lovers

Page 35: Kotlin: maybe it's the right time

Null is safe!val nullableList: List<Int?> = listOf(1, 2, null, 4)

val intList: List<Int> = nullableList.filterNotNull() // [1, 2, 4]

val aInt: Int? = b as? Int

// If b is null, normally we will will have a NullPointerException, while if the type is

differente we will have a ClassCastExeption. Usin “as?” we haven’t exception, we

will have a null value assigned to the aInt value.

Page 36: Kotlin: maybe it's the right time

Null è sicuro

data class Person(val name: String, val age: Int?)

val person:Person? = Person("Jack", 1)

if (person?.age != null) {

println("The person is aged ${person?.age}")

}

//oppure

person?.age?.let {

println("The person is aged $it")

}

Page 37: Kotlin: maybe it's the right time

?:Elvis operator

val davide = Person(testA)

val elvis = Person(testB ?: "Elvis")

println(jack.name)

Page 38: Kotlin: maybe it's the right time

Get & Set

class Strange(var value: Long) {

var strangeValue: Long

get() = value * 2

set(value){

if(value > 5) this.value = value

}

}

fun main(args: Array<String>) {

val customer = Strange(10)

println(customer.strangeValue) //20

customer.strangeValue = 3

println(customer.strangeValue) //20

customer.strangeValue = 6

println(customer.strangeValue) //12

}

Page 39: Kotlin: maybe it's the right time

Delegated properties: non può farlo qualcun altro?

class Delegate {

operator fun getValue(thisRef: Any?, property: KProperty<*>): String {

return "$thisRef, thank you for delegating '${property.name}' to me!"

}

operator fun setValue(thisRef: Any?, property: KProperty<*>, value: String) {

println("$value has been assigned to '${property.name} in $thisRef.'")

}

}

class Example {

var p: String by Delegate()

}

Page 40: Kotlin: maybe it's the right time

Delegated properties: il pigro e l’osservabile

val lazyValue: String by lazy {

println("computed!")

"Hello"

}

fun main(args: Array<String>) {

println(lazyValue)

println(lazyValue)

}

class User {

var n: String by Delegates.observable("empty") {

prop, old, new -> println("$old -> $new")

}

}

fun main(args: Array<String>) {

val user = User()

user.n = "first"

user.n = "second"

}

Page 41: Kotlin: maybe it's the right time

Coroutine

Coroutine ⋍ light-weight thread● Are like threads, they run in parallel, wait for each other and they communicate.● Area cheap, we can create many of those without having performance issues.● Are executed in a thread pool.● A thread can handle more than one coroutine.● The thread became free while a coroutine is in waiting when the coroutine will return active, it

will use a free thread in the pool.

https://github.com/Kotlin/kotlinx.coroutines/blob/master/coroutines-guide.mdhttps://proandroiddev.com/approaching-kotlin-coroutines-an-extensive-feature-concurrent-programming-in-kotlin-eaaa19b003d2

Page 42: Kotlin: maybe it's the right time

Coroutine sono oneste

fun main(args: Array<String>) = runBlocking {

val jobs = List(100_000) {

launch {

delay(1000L)

print(".")

}

}

jobs.forEach { it.join() }

}

fun main(args: Array<String>) {

val jobs = List(100_000) {

thread(start = true) {

Thread.sleep(1000L)

print(".")

}

}

jobs.forEach { it.join() }

}

OUT OF MEMORY!!!

Page 43: Kotlin: maybe it's the right time

Coroutine: suspend, async / awaitfun main(args: Array<String>) = runBlocking<Unit> { val time = measureTimeMillis { val one = doSomethingUsefulOne() val two = doSomethingUsefulTwo() println("The answer is ${one + two}") } println("Completed in $time ms")}

suspend fun doSomethingUsefulOne(): Int { delay(1000L) return 13}

suspend fun doSomethingUsefulTwo(): Int { delay(1000L) return 29}

fun main(args: Array<String>) = runBlocking<Unit> { val time = measureTimeMillis { val one = async { doSomethingUsefulOne() } val two = async { doSomethingUsefulTwo() } println("The answer is ${one.await() + two.await()}") } println("Completed in $time ms")}

suspend fun doSomethingUsefulOne(): Int { delay(1000L) return 13}

suspend fun doSomethingUsefulTwo(): Int { delay(1000L) return 29}

~2 sec.~1 sec.

Page 44: Kotlin: maybe it's the right time

Coroutine is the basement

Coroutine

ActorsCommunication Sequence Process

?

Page 45: Kotlin: maybe it's the right time

Testing

class GreeterTest { @Mock lateinit var user: User lateinit var tested: Greeter @Before fun setUp() { MockitoAnnotations.initMocks(this) tested = Greeter(user) } @Test fun englishGreetIsCorrect() { whenever(user.fullName()).thenReturn("Codemotion") assertEquals("Hello Codemotion!", tested.getGreeting()) }}

all values are final by default. With lateinit the compiler knows that someone will intialize it later.

Mockito.`when` is not the best choice with Kotlin. The cause is that Kotlin avoid null, and can cause issues with .any() mehtod. The “whenever” method is part of mockito-kotlin libary.

backticks are used when the method name is a reserved word.

Page 46: Kotlin: maybe it's the right time

Testing: Mockito-Kotlin

class GreeterTest {

val user: User = mock {

on { fullName() }.then { "Codemotion" }

}

val tested: Greeter = Greeter(user)

@Test

fun `english greeting is correct`() {

assertEquals("Hello, Codemotion!", tested.getGreeting())

}

}

Page 47: Kotlin: maybe it's the right time

Testing: Kluentclass GreeterTest3 {

val user: User = mock {

on { fullName() }.then { "Codemotion" }

}

val tested: Greeter = Greeter(user)

@Test

fun `english greeting is correct`() {

tested.getGreeting() `should equal` "Hello, Codemotion!"

}

@Test

fun `cannot smoke`() {

val davide = User("Davide", "Cerbo")

val davideSmoke = { davide.smoke() }

davideSmoke `should throw` RuntimeException::class `with message` "Cannot smoke!"

}

}

Page 48: Kotlin: maybe it's the right time

Testing

dependencies { compile "org.jetbrains.kotlin:kotlin-stdlib-jre8:$kotlin_version" testCompile "org.mockito:mockito-core:2.8.9" testCompile "org.jetbrains.kotlin:kotlin-test-junit:$kotlin_version" testCompile 'de.jodamob.kotlin:kotlin-runner-junit4:0.3.1' testCompile "com.nhaarman:mockito-kotlin-kt1.1:1.5.0" testCompile 'org.amshove.kluent:kluent:1.30'}

https://proandroiddev.com/improve-your-tests-with-kotlin-in-android-pt-1-6d0b04017e80https://proandroiddev.com/improve-your-tests-with-kotlin-in-android-pt-2-f3594e5e7bfdhttps://proandroiddev.com/improve-your-tests-with-kotlin-in-android-pt-3-104e1c9f77cc

Page 49: Kotlin: maybe it's the right time

Now is the time to remember that Kotlin is good because the trade-off between synthesis and

readable code is really good.

Page 50: Kotlin: maybe it's the right time

Automatic restart

Page 51: Kotlin: maybe it's the right time

Webapp: Gradlebuildscript {

...

dependencies {

...

classpath "org.jetbrains.kotlin:kotlin-noarg:$kotlinVersion"

}

}

...

apply plugin: "kotlin-jpa"

group = 'it.devday'

version = '0.0.1-SNAPSHOT'

...

}

JPA need an empty constructor in some entities, and Kotlin objects don’t have. To solve this issue we can use this plugin that will create an empty constructor in any object, without any change to our codebase.

Page 52: Kotlin: maybe it's the right time

Webapp: Domain & Repository

import it.devday.kotlincodemotion.domain.Contact

import org.springframework.data....JpaRepository

interface ContactRepository: JpaRepository<Contact, Long>

import javax.persistence.Entity

import javax.persistence.GeneratedValue

import javax.persistence.Id

@Entity

data class Contact(

@Id

@GeneratedValue

val id: Long,

val name: String,

val surname: String)

Page 53: Kotlin: maybe it's the right time

Webapp: Resource 1/2@RestController

@RequestMapping("/contacts")

class ContactResource(val contactRepository: ContactRepository) {

@GetMapping //curl -XGET http://localhost:8080/contacts

fun getAll() = contactRepository.findAll()

@GetMapping("/{id}") //curl -XGET http://localhost:8080/contacts/1

fun getAll(@PathVariable id: Long) = contactRepository.findById(id)

Page 54: Kotlin: maybe it's the right time

Webapp: Resource 2/2@PostMapping //curl -XPOST http://localhost:8080/contacts -H 'Content-Type: application/json' -d

'{"name":"Davide", "surname":"Cerbo"}'

fun insert(@RequestBody contact: Contact) = contactRepository.save(contact)

@DeleteMapping("/{id}") //curl -XDELETE http://localhost:8080/contacts/1

fun delete(@PathVariable id: Long) {

val contact = contactRepository.findById(id).unwrap()

contact?.let { contactRepository.delete(contact) }

}

}

aspetta...unwrap() non esiste su Optional

Page 55: Kotlin: maybe it's the right time

Webapp: Application@SpringBootApplication

class KotlinCodemotionApplication

fun <T> Optional<T>.unwrap(): T? = orElse(null)

fun main(args: Array<String>) {

runApplication<KotlinCodemotionApplication>(*args)

}

ecco unwrap()!

array is passed element by element, it is used with vararg argumentspublic inline fun <reified T : kotlin.Any> runApplication(vararg args: kotlin.String)

The generic type will be avaible in the method. Wow!

Page 56: Kotlin: maybe it's the right time

Hey, I’m a frontend developer, where is my code?

Page 57: Kotlin: maybe it's the right time

$ create-react-kotlin-app kotlin-codemotion-front

$ cd kotlin-codemotion-from

$ yarn start

https://github.com/jetbrains/create-react-kotlin-app

https://github.com/JetBrains/kotlin-wrappers/blob/master/kotlin-react/README.md

https://github.com/JetBrains/kotlin-wrappers/blob/master/kotlin-react-dom/README.md

https://github.com/Kotlin/kotlin-fullstack-sample

https://github.com/JetBrains/kotlinconf-app

https://blog.frankel.ch/kotlin-front-end-developers/#gsc.tab=0

https://github.com/sdeleuze/spring-kotlin-deepdive

Kotlin React: Application

Page 58: Kotlin: maybe it's the right time

React is a framework made by Facebook to build UI. Is the

View in the MVC pattern.

It simple associate a state to a specific UI. Your UI will change

regarding your state.

Kotlin React: Stop! What is React?

{name: “Davide”, color: “red”}

Hi Davide!

{name: “Jack”, color: “green”}

Hi Jack!

Page 59: Kotlin: maybe it's the right time

Kotlin React: Application: Component definitioninterface ContactListProps : RProps {

var owner: String

}

interface ContactListState : RState {

var list: Array<Contact>

}

class ContactList(props: ContactListProps) : RComponent<ContactListProps, ContactListState>(props) {

override fun ContactListState.init(props: ContactListProps) {

list = emptyArray()

}

...

Page 60: Kotlin: maybe it's the right time

Kotlin React: Application: Data loading override fun componentDidMount() {

async {

val listData = httpGet("/contacts")

setState { list = JSON.parse<Array<Contact>>(listData) }

}

}

remember to define the proxy in package.json:

"proxy": "http://localhost:8080"

Page 61: Kotlin: maybe it's the right time

Kotlin React: Application: UI rendering override fun RBuilder.render() {

div("ContactList-header") {

key = "contactListHeader"

h2 { +"${props.owner}'s contact list" }

ul {

for (c in state.list) {

li { +"${c.id}: ${c.name} ${c.surname}" }

}

}

} } }

fun RBuilder.contactList(owner: String = "No-name") = child(ContactList::class) {

attrs.owner = owner

}

Page 62: Kotlin: maybe it's the right time

STOP!

Page 63: Kotlin: maybe it's the right time

What we miss?

● Spring WebFlux● Reactor● Android + Kotlin● Native + Kotlin

#byebyejava

Page 64: Kotlin: maybe it's the right time

Everything is here:https://github.com/jesty/kotlin-fossavotabona

Page 65: Kotlin: maybe it's the right time

Questions

@davide_cerbo@devdayithttp://slack.devday.it

Page 66: Kotlin: maybe it's the right time

Useful resourceshttps://kotlinlang.org/docs/reference/

https://spring.io/blog/2017/01/04/introducing-kotlin-support-in-spring-framework-5-0

https://blog.jetbrains.com/kotlin/2017/05/kotlin-on-android-now-official/

https://dev.to/lovis/gang-of-four-patterns-in-kotlin

https://github.com/volodymyrprokopyuk/kotlin-sdp

https://github.com/gradle/gradle-script-kotlin

https://speakerdeck.com/sdeleuze/functional-web-applications-with-spring-and-kotlin

https://kotlinlang.org/docs/tutorials/httpservlets.html

http://thetechnocafe.com/more-about-functions-in-kotlin/

https://nklmish.wordpress.com/2017/10/22/deprecated-in-kotlin/

https://kotlinlang.org/docs/tutorials/command-line.html

https://agilewombat.com/2016/02/01/scala-vs-kotlin/

https://superkotlin.com/kotlin-vs-scala/

https://kotlinlang.org/docs/reference/type-safe-builders.html