Kotlin - Boost Your
Productivity
By Nklmish
@nklmish
Jetbrains
• Develops tools dedicated for
software developers.

• Large code base written in
Java (IDE and server side
tools). 

• E.g. IntelliJ-Community
edition, 3M+ lines of Java
code.
@nklmish
Back in 2010, Jetbrains were
looking for a language…
Java Interoperability
Can use existing Java code base & eco system
Reduce boilerplate code
Concise
Expressive
Easy tooling
Should be easy to prepare tooling for the language
Pragmatic
Solve real world problem
@nklmish
Why not X language ?
• “Most languages did not have the features we were
looking for, with the exception of Scala” - Dmitry Jemerov

• So why not Scala?

• Slow compilation time

• Tooling
@nklmish
Kotlin
• Open source programming language.

• Targets : Server side, client side web, mobile and
native.

• First commit - 8 Nov 2010, First release - 15 Feb
2016

• Gaining momentum - 20K+ repos and millions of lines
of code

• Google officially support it for android development.
@nklmish
Why Kotlin ?
Java Interoperability
We can use existing Java code & libraries as well

As call Java code from Kotlin and vice-versa
Null safety baked into type system
Safe
Statically Typed Easy tooling Pragmatic
Reduce boilerplate code
Concise
Expressive Small Learning Curve
Java developers can leverage there existing knowledge
Support Both
Let’s Get Started!
@nklmish
Hello
fun main(args: Array<String>) {
println("Hello")
}
@nklmish
Hello - source code level
fun main(args: Array<String>) {
println("Hello")
}
kotlin.Array
@nklmish
Hello - source code level
fun main(args: Array<String>) {
println("Hello")
}
kotlin.String
@nklmish
Hello - source code level
fun main(args: Array<String>) {
println("Hello")
}
kotlin.io.println
@nklmish
Hello- bytecode level
public final class com/nklmish/presentation/demo/s/_00Kt {
// access flags 0x19
public final static main([Ljava/lang/String;)V
@Lorg/jetbrains/annotations/NotNull;() // invisible, parameter 0
L0
ALOAD 0
LDC "args"
INVOKESTATIC kotlin/jvm/internal/Intrinsics.checkParameterIsNotNull (Ljava/lang/Object;Ljava/lang/String;)V
L1
LINENUMBER 7 L1
LDC "Hello"
ASTORE 1
L2
GETSTATIC java/lang/System.out : Ljava/io/PrintStream;
ALOAD 1
INVOKEVIRTUAL java/io/PrintStream.println (Ljava/lang/Object;)V
L3
L4
LINENUMBER 8 L4
RETURN
L5
LOCALVARIABLE args [Ljava/lang/String; L0 L5 0
MAXSTACK = 2
MAXLOCALS = 2
@Lkotlin/Metadata;(mv={1, 1, 7}, bv={1, 0, 2}, k=2,
d1={"u0000u0014nu0000nu0002u0010u0002nu0000nu0002u0010u0011nu0002u0010u000enu0002u0008u0002u001au0019u0010u0000u0
01au00020u00012u000cu0010u0002u001au0008u0012u0004u0012u00020u00040u0003u00a2u0006u0002u0010u0005u00a8u0006u0006"},
d2={"main", "", "args", "", "", "([Ljava/lang/String;)V", "production sources for module kotlin-presentation_main"})
// compiled from: 00.kt
// debug info: SMAP
00.kt
Kotlin
*S Kotlin
*F
+ 1 00.kt
com/nklmish/presentation/demo/s/_00Kt
*L
1#1,8:1
*E}
@nklmish
But how?
Let’s understand build process
@nklmish
Variable
@nklmish
List<Integer> list = new ArrayList<>(Arrays.asList(1, 2, 3));
val list: List<Int> = listOf(1, 2, 3)
Java Kotlin
Variable
@nklmish
List<Integer> list = new ArrayList<>(Arrays.asList(1, 2, 3));
val list: List<Int> = listOf(1, 2, 3)
Java Kotlin
Why Opposite ?
Variable
@nklmish
List<Integer> list = new ArrayList<>(Arrays.asList(1, 2, 3));
val list = listOf(1, 2, 3)
Java Kotlin
Type inference
Variable
@nklmish
List<Integer> list = new ArrayList<>(Arrays.asList(1, 2, 3));
val list = listOf(1, 2, 3)
Java Kotlin
IntelliJ view!
Function
@nklmish
Function
fun calculateBonus(base : Int, factor : Double) : Double {
return base * factor
}
@nklmish
Function
fun calculateBonus(base : Int, factor : Double) : Double {
return base * factor
}
Single Expression ?
@nklmish
Function
fun calculateBonus(base : Int, factor : Double) : Double {
return base * factor
}
Single Expression ?
Can be omitted
@nklmish
Function
fun calculateBonus(base : Int, factor : Double) : Double = base * factor
@nklmish
Function
fun calculateBonus(base : Int, factor : Double) : Double = base * factor
Type Inference
@nklmish
Function
fun calculateBonus(base : Int, factor : Double) = base * factor
@nklmish
Useful ?
Function
fun calculateBonus(base : Int, factor : Double) = base * factor
@nklmish
Concise +
refactor
Infix function == function
prefixed with “infix”
Must be a member/

extension function
@nklmish
Must take only one parameter
class Stack {
infix fun push(num : Int) {
//
}
}
fun main(args: Array<String>) {
val stack = Stack()
stack push 1
}
class Stack {
fun push(num : Int) {
//
}
}
fun main(args: Array<String>) {
val stack = Stack()
stack.push(1)
}
Infix function - real life
@nklmish
"should assign a default state for a newly launched game" {
game.states.count() shouldBe 1
}
val map = mapOf("Bob" to "123 North Avenue")
kotlintest
Kotlin
Extension functions ==
replace utility classes
@nklmish
fun LocalDateTime.toDate(): Date = Date.from(this.toInstant(OffsetDateTime.now().offset))
Higher order function
@nklmish
fun calculateTotal(amount : Double, exchange: (Double) -> Double) : Double {
val convertedAmount = exchange(amount)
return convertedAmount + (convertedAmount * 0.23)
}
Higher order function
@nklmish
fun calculateTotal(amount : Double, exchange: (Double) -> Double) : Double {
val convertedAmount = exchange(amount)
return convertedAmount + (convertedAmount * 0.23)
}
calculateTotal(100.00) {
amount -> amount * 4.5
}
calculateTotal(100.00) {
amount -> amount * 3.7
}
Invocation
Higher order function
fun calculateTotal(amount : Double, exchange: (Double) -> Double) : Double {
val convertedAmount = exchange(amount)
return convertedAmount + (convertedAmount * 0.23)
}
calculateTotal(100.00) {
amount -> amount * 4.5
}
calculateTotal(100.00) {
amount -> amount * 3.7
}
Lambda, extra cost (Wrapper Function), worried about runtime overhead ?
@nklmish
Inline function == avoid
runtime overhead
@nklmish
inline fun calculateTotal(amount : Double, exchange: (Double) -> Double) : Double {
val convertedAmount = exchange(amount)
return convertedAmount + (convertedAmount * 0.23)
}
Inline function == avoid
runtime overhead
@nklmish
inline fun calculateTotal(amount : Double, exchange: (Double) -> Double) : Double {
val convertedAmount = exchange(amount)
return convertedAmount + (convertedAmount * 0.23)
}
Compiler will replace this call with inline
(copy and paste) code
Inline function lambda
bytecode
@nklmish
inline fun calculateTotal(amount : Double, exchange: (Double) -> Double) : Double {
val convertedAmount = exchange(amount)
return convertedAmount + (convertedAmount * 0.23)
}
calculateTotal(100.00) {
amount -> amount * 4.5
}
calculateTotal(100.00) {
amount -> amount * 3.7
}
Inline function - time
measurement
@nklmish
val elapsedTime = measureTimeMillis { //execute the given code & returns elapsed time in ms.
calculateTotal(100.00) {
amount -> amount * 4.5
}
}
val elapsedTime = measureNanoTime { //execute the given block & returns elapsed time in ns.
...
}
Inline functions
Inline + reified == access
generic type info
@nklmish
@Suppress("UNCHECKED_CAST")
fun <T> Node.parent(clazz: Class<T>): T? {
var tmp = parent
while (tmp != null && !clazz.isInstance(tmp)) tmp = tmp.parent
return tmp as T?
}
//invocation
node.parent(Node::class.java)
Without reified
inline fun <reified T> Node.parent(): T? {
var tmp = parent
while (p != null && p !is T) tmp = tmp.parent
return p as T?
}
//invocation
node.parent<Node>()
Reified
Function literal with
receiver == recipe for DSL
@nklmish
class Delivery {
fun person(function: Person.() -> Unit) : Person {
...
}
fun address(function: Address.() -> Unit) : Address {
...
}
}
fun delivery(function: Delivery.() -> Unit): Delivery {
...
}
Extension fun on Delivery
Function literal with
receiver == recipe for DSL
@nklmish
class Delivery {
fun person(function: Person.() -> Unit) : Person {
...
}
fun address(function: Address.() -> Unit) : Address {
...
}
}
fun delivery(function: Delivery.() -> Unit): Delivery {
...
}
Extension fun on Delivery => we can access
Function literal with
receiver == recipe for DSL
@nklmish
delivery({ })
Syntactic sugar (if lambda is last argument)
delivery() { }== delivery { }==
Calling function “delivery” passing lambda as argument
Function literal with
receiver == recipe for DSL
@nklmish
Function literal with
receiver == recipe for DSL
@nklmish
class Delivery {
fun person(function: Person.() -> Unit) : Person {
...
}
fun address(function: Address.() -> Unit) : Address {
...
}
}
fun delivery(function: Delivery.() -> Unit): Delivery {
...
}
fun onTransit(function: () -> Unit) : TransitInfo {
…
}
fun sendSMS(message: String) {
...
}
Extension fun on Delivery
Extension fun on Address
Extension fun on Person
Higher Order Function
Function literal with
receiver == recipe for DSL
@nklmish
delivery {
person {
name = "Bob"
surname = "Smith"
}
address {
city = “paris"
street = "abc street"
postalCode = "12345"
onTransit {
sendSMS("...")
}
}
}
Final Result
Real life DSL - Anko
verticalLayout {
val name = editText()
button("Say Hello") {
onClick { toast("Hello, ${name.text}!") }
}
}
https://github.com/Kotlin/anko @nklmish
Real life DSL - GradleKotlinDSL
https://github.com/gradle/kotlin-dsl @nklmish
Overloading - Java
@nklmish
Overloading - Java
Which argument is which ?
@nklmish
Overloading - Kotlin
fun print(firstName : String, middleName: String = "", lastName : String = "") = { println("$firstName $middleName $lastName”) }
fun main(args: Array<String>) {
print(firstName = "john")
print(firstName = "john", lastName = "smith")
print(firstName = "john", middleName = "mac", lastName = "smith")
}
Default value Default value
@nklmish
Overloading - Kotlin
fun print(firstName : String, middleName: String = "", lastName : String = "") = println("$firstName $middleName $lastName")
fun main(args: Array<String>) {
print(firstName = "john")
print(firstName = "john", lastName = "smith")
print(firstName = "john", middleName = "mac", lastName = "smith")
}
Named arguments can further improve readability
Classes
@nklmish
Class - Java
public class Schedule {
private final LocalDateTime start;
private final LocalDateTime end;
private String notes;
public Schedule(LocalDateTime start, LocalDateTime end) {
this.start = start;
this.end = end;
}
public LocalDateTime getStart() {
return start;
}
public LocalDateTime getEnd() {
return end;
}
public String getNotes() {
return notes;
}
public void setNotes(String notes) {
this.notes = notes;
}
}
@nklmish
Class - Kotlin
class Schedule(val start: LocalDateTime, val end: LocalDateTime, var note : String)
@nklmish
Class - Kotlin
class Schedule(val start: LocalDateTime, val end: LocalDateTime, var note : String)
Default: public and final
@nklmish
Class - Kotlin
class Schedule(val start: LocalDateTime, val end: LocalDateTime, var note : String)
Properties
@nklmish
Class - Kotlin
class Schedule(val start: LocalDateTime, val end: LocalDateTime, var note : String)
Immutable Immutable
@nklmish
Class - Kotlin
class Schedule(val start: LocalDateTime, val end: LocalDateTime, var note : String)
Mutable
@nklmish
Data class == data holders
data class Schedule(val start : LocalDateTime, val end : LocalDateTime)
@nklmish
Data class
data class Schedule(val start : LocalDateTime, val end : LocalDateTime)
Autogenerate: meaningful toString(), copy(), equals(), hashcode()
@nklmish
Data class - copy()
val schedule = Schedule(LocalDateTime.now(), LocalDateTime.now().plusSeconds(1))
val copy = schedule.copy()
val copyWithModifiedEndDate = schedule.copy(end = schedule.end.plusSeconds(1)) // we
can change specific fields during copy, comes handy during unit testing
@nklmish
Data class - validation
data class Schedule(val start : LocalDateTime, val end : LocalDateTime) {
init {
if (end.isBefore(start)) throw IllegalArgumentException("end $end should be after start $start")
}
}
@nklmish
String interpolation
Note: String interpolation comes handy when dealing with JSON strings. e.g.
private val expectedServerContent = """
{"name":"$canonicalName","json":"{"id":"$expectedGameProviderId"}","data":{"@foo":".bar","number":"123"},"policy":"","password":""}
""".trimIndent()
Sealed class ~ extension of
enum classes
sealed class Bonus {
data class Yearly(val amount : BigDecimal) : Bonus()
data class Monthly(val amount : BigDecimal, val factor : BigDecimal) : Bonus()
}
@nklmish
Each enum constant exists only as 

a single instance
Enum
Subclass of a sealed class can have
multiple instances which can contain
state
Sealed class
Sealed class ~ extension of
enum classes
sealed class Bonus {
data class Yearly(val amount : BigDecimal) : Bonus()
data class Monthly(val amount : BigDecimal, val factor : BigDecimal) : Bonus()
}
fun evaluate(bonus: Bonus) = when(bonus) {
is Yearly -> bonus.amount
is Monthly -> bonus.amount.multiply(bonus.factor)
}
Abstract class
@nklmish
Delegation
@nklmish
Delegation
class NetworkChaosSimulator{
private ChaosSwitch chaosSwitch = new DefaultSwitch();
public void enableActivationChaos() {
chaosSwitch.enableActivationChaos();
}
public void disableActivationChaos() {
chaosSwitch.disableActivationChaos();
}
public void enableDeactivationChaos() {
chaosSwitch.enableDeactivationChaos();
}
public void disableDeactivationChaos() {
chaosSwitch.disableDeactivationChaos();
}
public boolean isActivationChaosEnabled() {
return chaosSwitch.isActivationChaosEnabled();
}
public boolean isDeactivationChaosEnabled() {
return chaosSwitch.isDeactivationChaosEnabled();
}
}
@nklmish
class NetworkChaosSimulator(switch: ChaosSwitch = DefaultSwitch())
: ChaosSwitch by switch
Java Kotlin
Delegated properties
• Wrap objects with custom behaviour.
@nklmish
Lazy
• Create a new instance only when an object get accessed
for the very first time.
@nklmish
Lazy
class GameRound(val player : String) {
val bets by lazy { fetchBets(this) }
private fun fetchBets(gameRound: GameRound): Int {
...
}
}
@nklmish
Lazy - thread safety ?
class GameRound(val player : String) {
val bets by lazy(mode = LazyThreadSafetyMode.SYNCHRONIZED) { fetchBets(this) }
private fun fetchBets(gameRound: GameRound): Int {
...
}
}
Only a single thread can initialise the Lazy instance.
@nklmish
Lazy - thread safety ?
class GameRound(val player : String) {
val bets by lazy(mode = LazyThreadSafetyMode.PUBLICATION) { fetchBets(this) }
private fun fetchBets(gameRound: GameRound): Int {
...
}
}
Concurrent access permissible but only first returned value will be used.
@nklmish
Lazy - thread safety ?
class GameRound(val player : String) {
val bets by lazy(mode = LazyThreadSafetyMode.NONE) { fetchBets(this) }
private fun fetchBets(gameRound: GameRound): Int {
...
}
}
multiple threads == undefined behaviour
@nklmish
There are more builtin delegated properties like notNull, observable, etc.
We can also create our own.
Lambda
@nklmish
players.filter({p -> p.score > 70})
@nklmish
players.filter({p -> p.score > 70})
players.filter() {p -> p.score > 70}
players.filter {p -> p.score > 70}
players.filter {it.score > 70}
syntactic sugar
Implicit name for a single parameter
@nklmish
• Allows to invoke methods of different object (w/o any
qualifiers) inside there body.
@nklmish@nklmish
with receiver
@nklmish
@nklmish@nklmish@nklmish
public String toCSVFormat(List<Integer> list) {
StringBuilder sb = new StringBuilder("[");
for (Integer integer : list) {
sb.append(integer).append(“;");
}
sb.deleteCharAt(sb.length() - 1);
sb.append("]");
return sb.toString();
}
Java
Kotlin : with (returns result of last call)
fun toCSVFormat(list: List<Int>) =
with(StringBuilder()) {
append("[")
for (i in list) {
append(i).append(";")
}
deleteCharAt(length - 1)
append("]")
toString()
}
with receiver - with
@nklmish@nklmish@nklmish
public String toCSVFormat(List<Integer> list) {
StringBuilder sb = new StringBuilder("[");
for (Integer integer : list) {
sb.append(integer).append(“;");
}
sb.deleteCharAt(sb.length() - 1);
sb.append("]");
return sb.toString();
}
Java Kotlin : apply (returns the receiver)
fun toCSVFormat(list: List<Int>) =
StringBuilder().apply {
append("[")
for (i in list) {
append(i).append(";")
}
deleteCharAt(length - 1)
append("]")
}.toString()
with receiver - apply
Safety
@nklmish
Null safety - baked into type
system
@nklmish
var message : String = "hello"
message = null // compile time error
var message : String? = "hello"
message = null // ok
println(message?.length) //safe operator, if not null then invoke length
Java
message?.let {
process(it)
}
Kotlin
if (message != null) {
process(message);
}
Null safety - using Java API
from Kotlin?
• Annotate Java code with nullability annotations.

• Kotlin can understand annotations from:

• Jetbrains (org.jetbrains.annotations)

• JSR-305 (javax.annotation)

• Android (android.support.annotation)
@nklmish
Misc
@nklmish
For loop - Java
for (int i = 0; i < 10; i++) {
System.out.println(i);
}
@nklmish
For loop - Kotlin
Progressions - to rescue
for (i in 0..9) println(i)
@nklmish
Progressions - to rescue
for (i in 0..9) println(i)
Lots of useful methods : downTo(), stepTo(), until…
@nklmish
Collection
• No Kotlin specific collections.

• Kotlin Collection == Java collection classes + enrichment.
@nklmish
Collection
Immutable
Mutable
@nklmish
Typealias
• Synonyms for pre-existing types
Typealias - without
fun main(args: Array<String>) {
val map : Map<String,String> = mapOf("Bob" to "North Avenue")
}
@nklmish
Less semantics
Typealias - with
typealias FirstName = String
typealias Street = String
fun main(args: Array<String>) {
val map : Map<FirstName, Street> = mapOf("Bob" to "North Avenue")
}
@nklmish
Better Semantics
Casting - Java
Object obj = "Any"
if (obj instanceof String) {
String str = (String) obj;
str.toUpperCase();
}
@nklmish
Casting - Kotlin (smartcast)
val obj:Any = "Any"
if (obj is String) {
obj.toUpperCase() // compiler smartness, auto cast to String
// Works only, if obj is immutable or 

// haven’t changed after check
}
@nklmish
Deprecation
@Deprecated(level = DeprecationLevel.WARNING,
message = "we are going to replace with StringUtils",
replaceWith = @ReplaceWith(
expression = "StringUtils.isEmpty(input)",
imports = {"org.apache.commons.lang3.StringUtils"})
)
public static boolean isEmpty(String input) {
...
}
@nklmish
Deprecation
@Deprecated(level = DeprecationLevel.WARNING,
message = "we are going to replace with StringUtils",
replaceWith = @ReplaceWith(
expression = "StringUtils.isEmpty(input)",
imports = {"org.apache.commons.lang3.StringUtils"})
)
public static boolean isEmpty(String input) {
...
}
level: Warning, Error, Hidden
@nklmish
Deprecation
@Deprecated(level = DeprecationLevel.WARNING,
message = "we are going to replace with StringUtils",
replaceWith = @ReplaceWith(
expression = "StringUtils.isEmpty(input)",
imports = {"org.apache.commons.lang3.StringUtils"})
)
public static boolean isEmpty(String input) {
...
}
Method to replace with
@nklmish
Deprecation
@Deprecated(level = DeprecationLevel.WARNING,
message = "we are going to replace with StringUtils",
replaceWith = @ReplaceWith(
expression = "StringUtils.isEmpty(input)",
imports = {"org.apache.commons.lang3.StringUtils"})
)
public static boolean isEmpty(String input) {
...
}
Which class to import
@nklmish
Deprecation - final result
@nklmish
when == powerful switch
fun playerPrivileges(rank : Int) = when(rank) {
in 1..3 -> "VIP"
in 4..50 -> "Elevated"
in 51..1000 -> "Classic"
else -> "Regular"
}
@nklmish
when == powerful switch
fun playerPrivileges(rank : Int) = when(rank) {
in 1..3 -> "VIP"
in 4..50 -> "Elevated"
in 51..1000 -> "Classic"
else -> "Regular"
}
Can be any type, no limitations
@nklmish
when == powerful switch
fun playerPrivileges(rank : Int) = when(rank) {
in 1..3 -> "VIP"
in 4..50 -> "Elevated"
in 51..1000 -> "Classic"
else -> "Regular"
}
No need to type break
@nklmish
Operator overloading -
Java?public class Wallet {
public Wallet(Integer amount) {
this.amount = amount;
}
private final Integer amount;
public Wallet plus(Wallet w) {
return new Wallet(this.amount + w.amount);
}
public Wallet minus(Wallet w) {
return new Wallet(this.amount - w.amount);
}
public Wallet multiply(Wallet w) {
return new Wallet(this.amount * w.amount);
}
}
Wallet walletA = new Wallet(100);
Wallet walletB = new Wallet(200);
walletA.plus(walletB);
walletA.subtract(walletB);
walletA.multiply(walletB);
@nklmish
Operator overloading -
Kotlin
data class Wallet(val amount : Int) {
operator fun plus(w : Wallet) = Wallet(amount.plus(w.amount))
operator fun minus(w : Wallet) = Wallet(amount.minus(w.amount))
operator fun times(w : Wallet) = Wallet(amount.minus(w.amount))
}
val walletA = Wallet(100)
val walletB = Wallet(200)
walletA + walletB
walletA - walletB
walletA * walletB
@nklmish
Operator overloading -
Kotlin
Naming Convention Syntax
plus walletA + walletB
unaryPlus +walletA
minus walletA - walletB
unaryMinus -walletA
inc ++walletA
dec — walletA
times walletA * walletB
div walletA / walletB
mod walletA % walletB
not !walletA
@nklmish
Co-routines == asyn
programming made simple
@nklmish
val jobs = List(1_000_000) {
async(CommonPool) {
delay(10L)
1.0
}
}
runBlocking {
println(jobs.sumByDouble { it.await() })
}
Co-routines == asyn
programming made simple
@nklmish
val jobs = List(1_000_000) {
async(CommonPool) {
delay(10L)
1.0
}
}
runBlocking {
println(jobs.sumByDouble { it.await() })
}
Lightweight
TODO
@nklmish
Kotlin + Spring
@nklmish
Kotlin + Spring
@nklmish
Kotlin + Spring -
build.gradle
@nklmish
dependencies {
classpath("org.jetbrains.kotlin:kotlin-allopen:$kotlinVersion")
}
Kotlin + Spring -
build.gradle
@nklmish
dependencies {
classpath("org.jetbrains.kotlin:kotlin-allopen:$kotlinVersion")
}
Before plugin
After Plugin
@SpringBootApplication
open class PhoneBookApplication {
@Bean
open fun passwordEncoder(): PasswordEncoder {
return BCryptPasswordEncoder()
}
}
@SpringBootApplication
class PhoneBookApplication {
@Bean
fun passwordEncoder(): PasswordEncoder {
return BCryptPasswordEncoder()
}
}
Kotlin + Spring -
build.gradle
@nklmish
dependencies {
classpath("org.jetbrains.kotlin:kotlin-allopen:$kotlinVersion")
}
@Document
data class Contact(@Id val id: String = UUID.randomUUID().toString(),
val firstName: String,
var lastName: String,
val phones: List<String> = emptyList()
)
Kotlin + Spring - controller
@nklmish
Kotlin + Spring - controller
@nklmish
Constructor injection(no @Autowired required)
Kotlin + Spring - controller
@nklmish
Type Inference
Kotlin + Spring - 

null safety?
@nklmish
@GetMapping("/schedule")
fun getSchedule(@RequestParam id: Long): Mono<ScheduleDto> =
scheduleService.findOne(id).map { it.get().dto() }
@GetMapping("/schedule")
fun getSchedule(@RequestParam id: Long?): Mono<ScheduleDto> =
scheduleService.findOne(id).map { it.get().dto() }
Kotlin + Spring - null safety
@nklmish
Kotlin + Spring
@nklmish
@GetMapping(path = arrayOf("/schedules"), produces = arrayOf(TEXT_EVENT_STREAM_VALUE))
fun getSchedules(): Flux<ScheduleDto> = scheduleService.findAll().map { it.dto() }
route(
path(“/schedules”).and(accept(TEXT_EVENT_STREAM_VALUE)),
)
Same thing expressed in Kotlin
Who uses Kotlin…
• Amazon

• Uber

• Netflix

• Foursquare

• Expedia

• HSBC

• Goldman Sachs

• Trello

• Casumo

• …
@nklmish
Useful links
• http://slack.kotlinlang.org/

• https://kotlin.link/
@nklmish
Questions?
https://tinyurl.com/y9lgkpc8
@nklmish
Review

Kotlin boost yourproductivity

  • 1.
    Kotlin - BoostYour Productivity By Nklmish @nklmish
  • 2.
    Jetbrains • Develops toolsdedicated for software developers. • Large code base written in Java (IDE and server side tools). • E.g. IntelliJ-Community edition, 3M+ lines of Java code. @nklmish
  • 3.
    Back in 2010,Jetbrains were looking for a language… Java Interoperability Can use existing Java code base & eco system Reduce boilerplate code Concise Expressive Easy tooling Should be easy to prepare tooling for the language Pragmatic Solve real world problem @nklmish
  • 4.
    Why not Xlanguage ? • “Most languages did not have the features we were looking for, with the exception of Scala” - Dmitry Jemerov • So why not Scala? • Slow compilation time • Tooling @nklmish
  • 5.
    Kotlin • Open sourceprogramming language. • Targets : Server side, client side web, mobile and native. • First commit - 8 Nov 2010, First release - 15 Feb 2016 • Gaining momentum - 20K+ repos and millions of lines of code • Google officially support it for android development. @nklmish
  • 6.
    Why Kotlin ? JavaInteroperability We can use existing Java code & libraries as well As call Java code from Kotlin and vice-versa Null safety baked into type system Safe Statically Typed Easy tooling Pragmatic Reduce boilerplate code Concise Expressive Small Learning Curve Java developers can leverage there existing knowledge Support Both
  • 7.
  • 8.
    Hello fun main(args: Array<String>){ println("Hello") } @nklmish
  • 9.
    Hello - sourcecode level fun main(args: Array<String>) { println("Hello") } kotlin.Array @nklmish
  • 10.
    Hello - sourcecode level fun main(args: Array<String>) { println("Hello") } kotlin.String @nklmish
  • 11.
    Hello - sourcecode level fun main(args: Array<String>) { println("Hello") } kotlin.io.println @nklmish
  • 12.
    Hello- bytecode level publicfinal class com/nklmish/presentation/demo/s/_00Kt { // access flags 0x19 public final static main([Ljava/lang/String;)V @Lorg/jetbrains/annotations/NotNull;() // invisible, parameter 0 L0 ALOAD 0 LDC "args" INVOKESTATIC kotlin/jvm/internal/Intrinsics.checkParameterIsNotNull (Ljava/lang/Object;Ljava/lang/String;)V L1 LINENUMBER 7 L1 LDC "Hello" ASTORE 1 L2 GETSTATIC java/lang/System.out : Ljava/io/PrintStream; ALOAD 1 INVOKEVIRTUAL java/io/PrintStream.println (Ljava/lang/Object;)V L3 L4 LINENUMBER 8 L4 RETURN L5 LOCALVARIABLE args [Ljava/lang/String; L0 L5 0 MAXSTACK = 2 MAXLOCALS = 2 @Lkotlin/Metadata;(mv={1, 1, 7}, bv={1, 0, 2}, k=2, d1={"u0000u0014nu0000nu0002u0010u0002nu0000nu0002u0010u0011nu0002u0010u000enu0002u0008u0002u001au0019u0010u0000u0 01au00020u00012u000cu0010u0002u001au0008u0012u0004u0012u00020u00040u0003u00a2u0006u0002u0010u0005u00a8u0006u0006"}, d2={"main", "", "args", "", "", "([Ljava/lang/String;)V", "production sources for module kotlin-presentation_main"}) // compiled from: 00.kt // debug info: SMAP 00.kt Kotlin *S Kotlin *F + 1 00.kt com/nklmish/presentation/demo/s/_00Kt *L 1#1,8:1 *E} @nklmish
  • 13.
  • 14.
    Let’s understand buildprocess @nklmish
  • 15.
    Variable @nklmish List<Integer> list =new ArrayList<>(Arrays.asList(1, 2, 3)); val list: List<Int> = listOf(1, 2, 3) Java Kotlin
  • 16.
    Variable @nklmish List<Integer> list =new ArrayList<>(Arrays.asList(1, 2, 3)); val list: List<Int> = listOf(1, 2, 3) Java Kotlin Why Opposite ?
  • 17.
    Variable @nklmish List<Integer> list =new ArrayList<>(Arrays.asList(1, 2, 3)); val list = listOf(1, 2, 3) Java Kotlin Type inference
  • 18.
    Variable @nklmish List<Integer> list =new ArrayList<>(Arrays.asList(1, 2, 3)); val list = listOf(1, 2, 3) Java Kotlin IntelliJ view!
  • 19.
  • 20.
    Function fun calculateBonus(base :Int, factor : Double) : Double { return base * factor } @nklmish
  • 21.
    Function fun calculateBonus(base :Int, factor : Double) : Double { return base * factor } Single Expression ? @nklmish
  • 22.
    Function fun calculateBonus(base :Int, factor : Double) : Double { return base * factor } Single Expression ? Can be omitted @nklmish
  • 23.
    Function fun calculateBonus(base :Int, factor : Double) : Double = base * factor @nklmish
  • 24.
    Function fun calculateBonus(base :Int, factor : Double) : Double = base * factor Type Inference @nklmish
  • 25.
    Function fun calculateBonus(base :Int, factor : Double) = base * factor @nklmish Useful ?
  • 26.
    Function fun calculateBonus(base :Int, factor : Double) = base * factor @nklmish Concise + refactor
  • 27.
    Infix function ==function prefixed with “infix” Must be a member/
 extension function @nklmish Must take only one parameter class Stack { infix fun push(num : Int) { // } } fun main(args: Array<String>) { val stack = Stack() stack push 1 } class Stack { fun push(num : Int) { // } } fun main(args: Array<String>) { val stack = Stack() stack.push(1) }
  • 28.
    Infix function -real life @nklmish "should assign a default state for a newly launched game" { game.states.count() shouldBe 1 } val map = mapOf("Bob" to "123 North Avenue") kotlintest Kotlin
  • 29.
    Extension functions == replaceutility classes @nklmish fun LocalDateTime.toDate(): Date = Date.from(this.toInstant(OffsetDateTime.now().offset))
  • 30.
    Higher order function @nklmish funcalculateTotal(amount : Double, exchange: (Double) -> Double) : Double { val convertedAmount = exchange(amount) return convertedAmount + (convertedAmount * 0.23) }
  • 31.
    Higher order function @nklmish funcalculateTotal(amount : Double, exchange: (Double) -> Double) : Double { val convertedAmount = exchange(amount) return convertedAmount + (convertedAmount * 0.23) } calculateTotal(100.00) { amount -> amount * 4.5 } calculateTotal(100.00) { amount -> amount * 3.7 } Invocation
  • 32.
    Higher order function funcalculateTotal(amount : Double, exchange: (Double) -> Double) : Double { val convertedAmount = exchange(amount) return convertedAmount + (convertedAmount * 0.23) } calculateTotal(100.00) { amount -> amount * 4.5 } calculateTotal(100.00) { amount -> amount * 3.7 } Lambda, extra cost (Wrapper Function), worried about runtime overhead ? @nklmish
  • 33.
    Inline function ==avoid runtime overhead @nklmish inline fun calculateTotal(amount : Double, exchange: (Double) -> Double) : Double { val convertedAmount = exchange(amount) return convertedAmount + (convertedAmount * 0.23) }
  • 34.
    Inline function ==avoid runtime overhead @nklmish inline fun calculateTotal(amount : Double, exchange: (Double) -> Double) : Double { val convertedAmount = exchange(amount) return convertedAmount + (convertedAmount * 0.23) } Compiler will replace this call with inline (copy and paste) code
  • 35.
    Inline function lambda bytecode @nklmish inlinefun calculateTotal(amount : Double, exchange: (Double) -> Double) : Double { val convertedAmount = exchange(amount) return convertedAmount + (convertedAmount * 0.23) } calculateTotal(100.00) { amount -> amount * 4.5 } calculateTotal(100.00) { amount -> amount * 3.7 }
  • 36.
    Inline function -time measurement @nklmish val elapsedTime = measureTimeMillis { //execute the given code & returns elapsed time in ms. calculateTotal(100.00) { amount -> amount * 4.5 } } val elapsedTime = measureNanoTime { //execute the given block & returns elapsed time in ns. ... } Inline functions
  • 37.
    Inline + reified== access generic type info @nklmish @Suppress("UNCHECKED_CAST") fun <T> Node.parent(clazz: Class<T>): T? { var tmp = parent while (tmp != null && !clazz.isInstance(tmp)) tmp = tmp.parent return tmp as T? } //invocation node.parent(Node::class.java) Without reified inline fun <reified T> Node.parent(): T? { var tmp = parent while (p != null && p !is T) tmp = tmp.parent return p as T? } //invocation node.parent<Node>() Reified
  • 38.
    Function literal with receiver== recipe for DSL @nklmish class Delivery { fun person(function: Person.() -> Unit) : Person { ... } fun address(function: Address.() -> Unit) : Address { ... } } fun delivery(function: Delivery.() -> Unit): Delivery { ... } Extension fun on Delivery
  • 39.
    Function literal with receiver== recipe for DSL @nklmish class Delivery { fun person(function: Person.() -> Unit) : Person { ... } fun address(function: Address.() -> Unit) : Address { ... } } fun delivery(function: Delivery.() -> Unit): Delivery { ... } Extension fun on Delivery => we can access
  • 40.
    Function literal with receiver== recipe for DSL @nklmish delivery({ }) Syntactic sugar (if lambda is last argument) delivery() { }== delivery { }== Calling function “delivery” passing lambda as argument
  • 41.
    Function literal with receiver== recipe for DSL @nklmish
  • 42.
    Function literal with receiver== recipe for DSL @nklmish class Delivery { fun person(function: Person.() -> Unit) : Person { ... } fun address(function: Address.() -> Unit) : Address { ... } } fun delivery(function: Delivery.() -> Unit): Delivery { ... } fun onTransit(function: () -> Unit) : TransitInfo { … } fun sendSMS(message: String) { ... } Extension fun on Delivery Extension fun on Address Extension fun on Person Higher Order Function
  • 43.
    Function literal with receiver== recipe for DSL @nklmish delivery { person { name = "Bob" surname = "Smith" } address { city = “paris" street = "abc street" postalCode = "12345" onTransit { sendSMS("...") } } } Final Result
  • 44.
    Real life DSL- Anko verticalLayout { val name = editText() button("Say Hello") { onClick { toast("Hello, ${name.text}!") } } } https://github.com/Kotlin/anko @nklmish
  • 45.
    Real life DSL- GradleKotlinDSL https://github.com/gradle/kotlin-dsl @nklmish
  • 46.
  • 47.
    Overloading - Java Whichargument is which ? @nklmish
  • 48.
    Overloading - Kotlin funprint(firstName : String, middleName: String = "", lastName : String = "") = { println("$firstName $middleName $lastName”) } fun main(args: Array<String>) { print(firstName = "john") print(firstName = "john", lastName = "smith") print(firstName = "john", middleName = "mac", lastName = "smith") } Default value Default value @nklmish
  • 49.
    Overloading - Kotlin funprint(firstName : String, middleName: String = "", lastName : String = "") = println("$firstName $middleName $lastName") fun main(args: Array<String>) { print(firstName = "john") print(firstName = "john", lastName = "smith") print(firstName = "john", middleName = "mac", lastName = "smith") } Named arguments can further improve readability
  • 50.
  • 51.
    Class - Java publicclass Schedule { private final LocalDateTime start; private final LocalDateTime end; private String notes; public Schedule(LocalDateTime start, LocalDateTime end) { this.start = start; this.end = end; } public LocalDateTime getStart() { return start; } public LocalDateTime getEnd() { return end; } public String getNotes() { return notes; } public void setNotes(String notes) { this.notes = notes; } } @nklmish
  • 52.
    Class - Kotlin classSchedule(val start: LocalDateTime, val end: LocalDateTime, var note : String) @nklmish
  • 53.
    Class - Kotlin classSchedule(val start: LocalDateTime, val end: LocalDateTime, var note : String) Default: public and final @nklmish
  • 54.
    Class - Kotlin classSchedule(val start: LocalDateTime, val end: LocalDateTime, var note : String) Properties @nklmish
  • 55.
    Class - Kotlin classSchedule(val start: LocalDateTime, val end: LocalDateTime, var note : String) Immutable Immutable @nklmish
  • 56.
    Class - Kotlin classSchedule(val start: LocalDateTime, val end: LocalDateTime, var note : String) Mutable @nklmish
  • 57.
    Data class ==data holders data class Schedule(val start : LocalDateTime, val end : LocalDateTime) @nklmish
  • 58.
    Data class data classSchedule(val start : LocalDateTime, val end : LocalDateTime) Autogenerate: meaningful toString(), copy(), equals(), hashcode() @nklmish
  • 59.
    Data class -copy() val schedule = Schedule(LocalDateTime.now(), LocalDateTime.now().plusSeconds(1)) val copy = schedule.copy() val copyWithModifiedEndDate = schedule.copy(end = schedule.end.plusSeconds(1)) // we can change specific fields during copy, comes handy during unit testing @nklmish
  • 60.
    Data class -validation data class Schedule(val start : LocalDateTime, val end : LocalDateTime) { init { if (end.isBefore(start)) throw IllegalArgumentException("end $end should be after start $start") } } @nklmish String interpolation Note: String interpolation comes handy when dealing with JSON strings. e.g. private val expectedServerContent = """ {"name":"$canonicalName","json":"{"id":"$expectedGameProviderId"}","data":{"@foo":".bar","number":"123"},"policy":"","password":""} """.trimIndent()
  • 61.
    Sealed class ~extension of enum classes sealed class Bonus { data class Yearly(val amount : BigDecimal) : Bonus() data class Monthly(val amount : BigDecimal, val factor : BigDecimal) : Bonus() } @nklmish Each enum constant exists only as 
 a single instance Enum Subclass of a sealed class can have multiple instances which can contain state Sealed class
  • 62.
    Sealed class ~extension of enum classes sealed class Bonus { data class Yearly(val amount : BigDecimal) : Bonus() data class Monthly(val amount : BigDecimal, val factor : BigDecimal) : Bonus() } fun evaluate(bonus: Bonus) = when(bonus) { is Yearly -> bonus.amount is Monthly -> bonus.amount.multiply(bonus.factor) } Abstract class @nklmish
  • 63.
  • 64.
    Delegation class NetworkChaosSimulator{ private ChaosSwitchchaosSwitch = new DefaultSwitch(); public void enableActivationChaos() { chaosSwitch.enableActivationChaos(); } public void disableActivationChaos() { chaosSwitch.disableActivationChaos(); } public void enableDeactivationChaos() { chaosSwitch.enableDeactivationChaos(); } public void disableDeactivationChaos() { chaosSwitch.disableDeactivationChaos(); } public boolean isActivationChaosEnabled() { return chaosSwitch.isActivationChaosEnabled(); } public boolean isDeactivationChaosEnabled() { return chaosSwitch.isDeactivationChaosEnabled(); } } @nklmish class NetworkChaosSimulator(switch: ChaosSwitch = DefaultSwitch()) : ChaosSwitch by switch Java Kotlin
  • 65.
    Delegated properties • Wrapobjects with custom behaviour. @nklmish
  • 66.
    Lazy • Create anew instance only when an object get accessed for the very first time. @nklmish
  • 67.
    Lazy class GameRound(val player: String) { val bets by lazy { fetchBets(this) } private fun fetchBets(gameRound: GameRound): Int { ... } } @nklmish
  • 68.
    Lazy - threadsafety ? class GameRound(val player : String) { val bets by lazy(mode = LazyThreadSafetyMode.SYNCHRONIZED) { fetchBets(this) } private fun fetchBets(gameRound: GameRound): Int { ... } } Only a single thread can initialise the Lazy instance. @nklmish
  • 69.
    Lazy - threadsafety ? class GameRound(val player : String) { val bets by lazy(mode = LazyThreadSafetyMode.PUBLICATION) { fetchBets(this) } private fun fetchBets(gameRound: GameRound): Int { ... } } Concurrent access permissible but only first returned value will be used. @nklmish
  • 70.
    Lazy - threadsafety ? class GameRound(val player : String) { val bets by lazy(mode = LazyThreadSafetyMode.NONE) { fetchBets(this) } private fun fetchBets(gameRound: GameRound): Int { ... } } multiple threads == undefined behaviour @nklmish There are more builtin delegated properties like notNull, observable, etc. We can also create our own.
  • 71.
  • 72.
  • 73.
    players.filter({p -> p.score> 70}) players.filter() {p -> p.score > 70} players.filter {p -> p.score > 70} players.filter {it.score > 70} syntactic sugar Implicit name for a single parameter @nklmish
  • 74.
    • Allows toinvoke methods of different object (w/o any qualifiers) inside there body. @nklmish@nklmish with receiver @nklmish
  • 75.
    @nklmish@nklmish@nklmish public String toCSVFormat(List<Integer>list) { StringBuilder sb = new StringBuilder("["); for (Integer integer : list) { sb.append(integer).append(“;"); } sb.deleteCharAt(sb.length() - 1); sb.append("]"); return sb.toString(); } Java Kotlin : with (returns result of last call) fun toCSVFormat(list: List<Int>) = with(StringBuilder()) { append("[") for (i in list) { append(i).append(";") } deleteCharAt(length - 1) append("]") toString() } with receiver - with
  • 76.
    @nklmish@nklmish@nklmish public String toCSVFormat(List<Integer>list) { StringBuilder sb = new StringBuilder("["); for (Integer integer : list) { sb.append(integer).append(“;"); } sb.deleteCharAt(sb.length() - 1); sb.append("]"); return sb.toString(); } Java Kotlin : apply (returns the receiver) fun toCSVFormat(list: List<Int>) = StringBuilder().apply { append("[") for (i in list) { append(i).append(";") } deleteCharAt(length - 1) append("]") }.toString() with receiver - apply
  • 77.
  • 78.
    Null safety -baked into type system @nklmish var message : String = "hello" message = null // compile time error var message : String? = "hello" message = null // ok println(message?.length) //safe operator, if not null then invoke length Java message?.let { process(it) } Kotlin if (message != null) { process(message); }
  • 79.
    Null safety -using Java API from Kotlin? • Annotate Java code with nullability annotations. • Kotlin can understand annotations from: • Jetbrains (org.jetbrains.annotations) • JSR-305 (javax.annotation) • Android (android.support.annotation) @nklmish
  • 80.
  • 81.
    For loop -Java for (int i = 0; i < 10; i++) { System.out.println(i); } @nklmish
  • 82.
    For loop -Kotlin
  • 83.
    Progressions - torescue for (i in 0..9) println(i) @nklmish
  • 84.
    Progressions - torescue for (i in 0..9) println(i) Lots of useful methods : downTo(), stepTo(), until… @nklmish
  • 85.
    Collection • No Kotlinspecific collections. • Kotlin Collection == Java collection classes + enrichment. @nklmish
  • 86.
  • 87.
    Typealias • Synonyms forpre-existing types
  • 88.
    Typealias - without funmain(args: Array<String>) { val map : Map<String,String> = mapOf("Bob" to "North Avenue") } @nklmish Less semantics
  • 89.
    Typealias - with typealiasFirstName = String typealias Street = String fun main(args: Array<String>) { val map : Map<FirstName, Street> = mapOf("Bob" to "North Avenue") } @nklmish Better Semantics
  • 90.
    Casting - Java Objectobj = "Any" if (obj instanceof String) { String str = (String) obj; str.toUpperCase(); } @nklmish
  • 91.
    Casting - Kotlin(smartcast) val obj:Any = "Any" if (obj is String) { obj.toUpperCase() // compiler smartness, auto cast to String // Works only, if obj is immutable or 
 // haven’t changed after check } @nklmish
  • 92.
    Deprecation @Deprecated(level = DeprecationLevel.WARNING, message= "we are going to replace with StringUtils", replaceWith = @ReplaceWith( expression = "StringUtils.isEmpty(input)", imports = {"org.apache.commons.lang3.StringUtils"}) ) public static boolean isEmpty(String input) { ... } @nklmish
  • 93.
    Deprecation @Deprecated(level = DeprecationLevel.WARNING, message= "we are going to replace with StringUtils", replaceWith = @ReplaceWith( expression = "StringUtils.isEmpty(input)", imports = {"org.apache.commons.lang3.StringUtils"}) ) public static boolean isEmpty(String input) { ... } level: Warning, Error, Hidden @nklmish
  • 94.
    Deprecation @Deprecated(level = DeprecationLevel.WARNING, message= "we are going to replace with StringUtils", replaceWith = @ReplaceWith( expression = "StringUtils.isEmpty(input)", imports = {"org.apache.commons.lang3.StringUtils"}) ) public static boolean isEmpty(String input) { ... } Method to replace with @nklmish
  • 95.
    Deprecation @Deprecated(level = DeprecationLevel.WARNING, message= "we are going to replace with StringUtils", replaceWith = @ReplaceWith( expression = "StringUtils.isEmpty(input)", imports = {"org.apache.commons.lang3.StringUtils"}) ) public static boolean isEmpty(String input) { ... } Which class to import @nklmish
  • 96.
    Deprecation - finalresult @nklmish
  • 97.
    when == powerfulswitch fun playerPrivileges(rank : Int) = when(rank) { in 1..3 -> "VIP" in 4..50 -> "Elevated" in 51..1000 -> "Classic" else -> "Regular" } @nklmish
  • 98.
    when == powerfulswitch fun playerPrivileges(rank : Int) = when(rank) { in 1..3 -> "VIP" in 4..50 -> "Elevated" in 51..1000 -> "Classic" else -> "Regular" } Can be any type, no limitations @nklmish
  • 99.
    when == powerfulswitch fun playerPrivileges(rank : Int) = when(rank) { in 1..3 -> "VIP" in 4..50 -> "Elevated" in 51..1000 -> "Classic" else -> "Regular" } No need to type break @nklmish
  • 100.
    Operator overloading - Java?publicclass Wallet { public Wallet(Integer amount) { this.amount = amount; } private final Integer amount; public Wallet plus(Wallet w) { return new Wallet(this.amount + w.amount); } public Wallet minus(Wallet w) { return new Wallet(this.amount - w.amount); } public Wallet multiply(Wallet w) { return new Wallet(this.amount * w.amount); } } Wallet walletA = new Wallet(100); Wallet walletB = new Wallet(200); walletA.plus(walletB); walletA.subtract(walletB); walletA.multiply(walletB); @nklmish
  • 101.
    Operator overloading - Kotlin dataclass Wallet(val amount : Int) { operator fun plus(w : Wallet) = Wallet(amount.plus(w.amount)) operator fun minus(w : Wallet) = Wallet(amount.minus(w.amount)) operator fun times(w : Wallet) = Wallet(amount.minus(w.amount)) } val walletA = Wallet(100) val walletB = Wallet(200) walletA + walletB walletA - walletB walletA * walletB @nklmish
  • 102.
    Operator overloading - Kotlin NamingConvention Syntax plus walletA + walletB unaryPlus +walletA minus walletA - walletB unaryMinus -walletA inc ++walletA dec — walletA times walletA * walletB div walletA / walletB mod walletA % walletB not !walletA @nklmish
  • 103.
    Co-routines == asyn programmingmade simple @nklmish val jobs = List(1_000_000) { async(CommonPool) { delay(10L) 1.0 } } runBlocking { println(jobs.sumByDouble { it.await() }) }
  • 104.
    Co-routines == asyn programmingmade simple @nklmish val jobs = List(1_000_000) { async(CommonPool) { delay(10L) 1.0 } } runBlocking { println(jobs.sumByDouble { it.await() }) } Lightweight
  • 105.
  • 106.
  • 107.
  • 108.
    Kotlin + Spring- build.gradle @nklmish dependencies { classpath("org.jetbrains.kotlin:kotlin-allopen:$kotlinVersion") }
  • 109.
    Kotlin + Spring- build.gradle @nklmish dependencies { classpath("org.jetbrains.kotlin:kotlin-allopen:$kotlinVersion") } Before plugin After Plugin @SpringBootApplication open class PhoneBookApplication { @Bean open fun passwordEncoder(): PasswordEncoder { return BCryptPasswordEncoder() } } @SpringBootApplication class PhoneBookApplication { @Bean fun passwordEncoder(): PasswordEncoder { return BCryptPasswordEncoder() } }
  • 110.
    Kotlin + Spring- build.gradle @nklmish dependencies { classpath("org.jetbrains.kotlin:kotlin-allopen:$kotlinVersion") } @Document data class Contact(@Id val id: String = UUID.randomUUID().toString(), val firstName: String, var lastName: String, val phones: List<String> = emptyList() )
  • 111.
    Kotlin + Spring- controller @nklmish
  • 112.
    Kotlin + Spring- controller @nklmish Constructor injection(no @Autowired required)
  • 113.
    Kotlin + Spring- controller @nklmish Type Inference
  • 114.
    Kotlin + Spring- 
 null safety? @nklmish @GetMapping("/schedule") fun getSchedule(@RequestParam id: Long): Mono<ScheduleDto> = scheduleService.findOne(id).map { it.get().dto() } @GetMapping("/schedule") fun getSchedule(@RequestParam id: Long?): Mono<ScheduleDto> = scheduleService.findOne(id).map { it.get().dto() }
  • 115.
    Kotlin + Spring- null safety @nklmish
  • 116.
    Kotlin + Spring @nklmish @GetMapping(path= arrayOf("/schedules"), produces = arrayOf(TEXT_EVENT_STREAM_VALUE)) fun getSchedules(): Flux<ScheduleDto> = scheduleService.findAll().map { it.dto() } route( path(“/schedules”).and(accept(TEXT_EVENT_STREAM_VALUE)), ) Same thing expressed in Kotlin
  • 117.
    Who uses Kotlin… •Amazon • Uber • Netflix • Foursquare • Expedia • HSBC • Goldman Sachs • Trello • Casumo • … @nklmish
  • 118.
  • 119.