Date post: | 21-Jan-2018 |
Category: |
Software |
Upload: | davide-cerbo |
View: | 215 times |
Download: | 1 times |
Kotlin: maybe it’s the right timeDavide Cerbo - Entaksi
CODEMOTION MILAN - SPECIAL EDITION 10 – 11 NOVEMBER 2017
Chi sono
Hi, my name is Davide and I’m writing bad code since I was 7. Since 2003 I’m paid for that.
DisclaimerHere we have a lot of println, and some jokes.
My wife said that they aren’t funny.
C’era una volta OAK
It was 1992 and in Italy there was a big scandal: Tangentopoli.
C’era una volta OAK Java
Borns in 1995 and now can drink beers.
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!
JVM != Java
JAVA JVM
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
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
Why Kotlin?
Hey! People said the same about Scala in the past. So why not Scala?
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/
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.
https://dzone.com/articles/why-learn-kotlin-infographic
Since 2010(2012): KOTLIN!
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
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/
5 Giugno 2017kotlin-react borns!
https://github.com/JetBrains/kotlin-wrappers/commits/master/kotlin-react
https://github.com/JetBrains/create-react-kotlin-app
1.0 Release
1.1 Release
Kotlin is born
Gradle
Android Official
Spring
Statische download
Please shutdown the lights
Var o Val
var serie = "B"
var a = "Salernitana will win Serie $serie"
val b = "Salernitana will win Serie $serie"
Fun
fun main(args: Array<String>){
hello("Davide", "Salerno")
}
fun hello(name: String, city: String){
println("Hello $name from $city")
}
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!
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)
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
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
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()
}
Class
class Customer(name: String) : Person(name) {
override fun speak() {
println("Welcome $name!")
}
}
class CustomerDavide : Person("Davide") {
override fun speak() {
println("Welcome $name!")
}
}
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
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(...)
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) }
}
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.
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.
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"))
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
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.
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")
}
?:Elvis operator
val davide = Person(testA)
val elvis = Person(testB ?: "Elvis")
println(jack.name)
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
}
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()
}
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"
}
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
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!!!
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.
Coroutine is the basement
Coroutine
ActorsCommunication Sequence Process
?
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.
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())
}
}
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!"
}
}
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
Now is the time to remember that Kotlin is good because the trade-off between synthesis and
readable code is really good.
Automatic restart
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.
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)
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)
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
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!
Hey, I’m a frontend developer, where is my code?
$ 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
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!
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()
}
...
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"
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
}
STOP!
What we miss?
● Spring WebFlux● Reactor● Android + Kotlin● Native + Kotlin
#byebyejava
Everything is here:https://github.com/jesty/kotlin-fossavotabona
Questions
@davide_cerbo@devdayithttp://slack.devday.it
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