After 20 years, Java seems to feel the weight of the years and his syntax it isn't evolved as we live. But the JVM it is always a stable environment and it is often in production everywhere. In the past years, there were many languages based on the JVM, but they don't have a lot of success. Kotlin conquered Android and, now, thanks to an easy syntax many and big companies behind it, may be used also in web development, also as full stack language. During this talk, we will give a look the language and how to use it to develop a production ready web application.
All code showed during presentation is here: https://github.com/jesty/kotlin-fossavotabona
Powering Real-Time Decisions with Continuous Data Streams
Kotlin: maybe it's the right time
1. Kotlin: maybe it’s the right time
Davide Cerbo - Entaksi
CODEMOTION MILAN - SPECIAL EDITION
10 – 11 NOVEMBER 2017
2. Chi sono
Hi, my name is Davide and I’m
writing bad code since I was 7.
Since 2003 I’m paid for that.
3. Disclaimer
Here we have a lot of println, and some jokes.
My wife said that they aren’t funny.
4. C’era una volta OAK
It was 1992 and in Italy there
was a big scandal: Tangentopoli.
5. C’era una volta OAK Java
Borns in 1995
and now can
drink beers.
6. 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!
8. 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={"u0000u0012nu0002u0018u0002nu0002u0010u0000nu0002
u0008u0002nu0002u0010u000enu0000u0018u00002u00020u00
01Bu0005u00a2u0006u0002u0010u0002Ju0006u0010u0003u001
au00020u0004u00a8u0006u0005", 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 =
{"u0000u0012nu0002u0018u0002nu0002
u0005¢u0006u0002u0010u0002Ju0006u00
d2 = {"Ltotest/Hello;", "", "()V", "sayHello", "", "
)
public final class Hello {
@NotNull
public final String sayHello() {
return "hello!";
}
}
Compile Decompile
9. Machine code is not enought?
Assembly languages
Machine code
Logic languages
(Prolog)
Functional languages
(Haskell)
Procedural languages
(C)
Object-oriented languages
(Java)
The Case for Kotlin and Ceylon by Russel Winder
https://www.youtube.com/watch?v=cFL_DDXBkJQ
11. 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/
12.
13. 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.
15. 04 Gennaio 2017
Introducing Kotlin support
in Spring Framework 5.0
https://spring.io/blog/2017/01/04/introducing-kotlin-support-in-spring-framework-5-0
https://github.com/sdeleuze/spring-kotlin-deepdive
16. 17 Maggio 2017
Kotlin on Android. Now official
https://blog.jetbrains.com/kotlin/2017/05/kotlin-on-android-now-official/
http://nilhcem.com/swift-is-like-kotlin/
17. 5 Giugno 2017
kotlin-react borns!
https://github.com/JetBrains/kotlin-wrappers/commits/master/kotlin-react
https://github.com/JetBrains/create-react-kotlin-app
22. 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!
23. 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)
24. 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
25. Tailrec decompiled
public 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
Tailrec
Without
Tailrec
StackOverflow
Tools > Kotlin > Show Kotlin Bytecode > Decompile
26. 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()
}
27. Class
class Customer(name: String) : Person(name) {
override fun speak() {
println("Welcome $name!")
}
}
class CustomerDavide : Person("Davide") {
override fun speak() {
println("Welcome $name!")
}
}
28. 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
29. 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(...)
30. 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) }
}
31. 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.
32. 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.
33. 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"))
34. 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
35. 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.
36. 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")
}
38. 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
}
39. 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()
}
40. 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"
}
41. 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.md
https://proandroiddev.com/approaching-kotlin-coroutines-an-extensive-feature-concurrent-programming-in-kotlin-eaaa19b003d2
42. 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!!!
43. Coroutine: suspend, async / await
fun 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.
44. Coroutine is the basement
Coroutine
Actors
Communication
Sequence
Process
?
45. 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.
46. 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())
}
}
47. Testing: Kluent
class 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!"
}
}
51. Webapp: Gradle
buildscript {
...
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.
52. 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)
53. 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)
54. 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
55. 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 arguments
public inline fun <reified T : kotlin.Any> runApplication(vararg args: kotlin.String)
The generic type will be avaible in the method. Wow!
56. Hey, I’m a frontend developer,
where is my code?
58. 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!
59. Kotlin React: Application: Component definition
interface 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()
}
...
60. 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"
61. 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
}