What is Kotlin
- A programming language
- Created by JetBrains
- Strong Typed
- C++/Java looks-like
- Concise
- Not a paradigm shift
- Modern constructs
- LLVM compiler
Can produce code for
- JVM
- JavaScript
- Native
- applications
- imports/export C libraries
- WebAssembly
- Cross platform modules
What it looks like
fun main(args : Array<String>) {
println("Hello, world!")
}
Kotlin provides several features
let's review some of them
Null Safety
var a: String = "abc"
a = null // compilation error
var b: String? = "abc"
b = null // ok
val l = b?.length ?: -1
Object Oriented
class Greeter(val name: String) {
fun greet() {
println("Hello, $name")
}
}
fun main(args: Array<String>) {
Greeter(args[0]).greet()
}
Type inference
val a = "abc"
val b = 1
fun getData() : String {
return "hello world"
}
val c = getData()
print(c) // will print "hello world"
fun getData() : Customer {
return Customer(1, "super customer") //a class that have id, name
}
print(c) // will print "Customer(id=1, name=super customer)"
Single expression functions
fun sum(a : Int, b : Int) : Int = a + b
fun sum2(a : Int, b : Int) = a + b
fun bigger(a : Int, b : Int) = if (a>b) a else b
val value = if (bigger(x, y)==x) z else 1-z
val other = when(getValue(x, y)){
0, 1 -> 0
-1 -> y
else -> x
}
Default and named arguments
fun displayBorder(character: Char = '=', length: Int = 15) {
for (i in 1..length) {
print(character)
}
println("")
}
fun main(args : Array<String>) {
displayBorder() //print: ===============
displayBorder('*') //print: ***************
displayBorder('*', 5) //print: *****
displayBorder(length = 2) //print: ==
}
Data classes
// - equals() / hashCode()
// - toString() of the form "User(name=John, age=42)"
// - copy()
// - componentN() : component1() = name, component2() = age
data class User(val name: String = "", val age: Int = 0)
val user = User("John", "42")
val olderUser = user.copy(age=50);
val name, age = user;
fun getUser() = User("John", "42")
val userName, userAge = getUser()
Extensions functions
fun MutableList<Int>.swap(index1: Int, index2: Int) {
val tmp = this[index1] // 'this' corresponds to the list
this[index1] = this[index2]
this[index2] = tmp
}
val l = mutableListOf(1, 2, 3)
l.swap(0, 2) // the list will be (3, 2, 1)
Lambdas
val strings = listOf("hello", "I'm", "a list", "of", "strings")
strings.filter {
value : String -> value.length > 3
}.forEach{ value : String -> println(value) }
strings.filter{
value -> value.length > 3
}.forEach{ value -> println(value) }
strings.filter { it.length > 3 }.forEach(::println)
Type-Safe builders
fun result(args: Array<String>) =
house {
rooms {
room {
person {+"Lonley"}
}
room {
person {
for (arg in args)
+arg
}
}
}
Lines of Code |
Source |
Tests |
Java |
1004 |
1172 |
Kotlin |
721 |
1017 |
createResult() Java vs Kotlin
Mono<SunriseSunset> createResult(final Mono<GeoTimesResponse> geoTimesResponseMono) {
return geoTimesResponseMono.flatMap(geoTimesResponse -> {
if ((geoTimesResponse.getStatus() != null) && (geoTimesResponse.getStatus()
.equals(STATUS_OK))) {
return Mono.just(new SunriseSunset(geoTimesResponse.getResults().getSunrise(),
geoTimesResponse.getResults().getSunset()));
} else {
return Mono.error(new GetSunriseSunsetException(SUNRISE_RESULT_NOT_OK));
}
});
}
open internal fun createResult(geoTimesResponseMono: Mono<GeoTimesResponse>) =
geoTimesResponseMono.flatMap {
with(it){
if (status == STATUS_OK) with(results) { SunriseSunset(sunrise, sunset).toMono() }
else GetSunriseSunsetException(SUNRISE_RESULT_NOT_OK).toMono()
}
}
Kotlin is not only about shortening our code, is about reducing the boilerplate, making our code more comprehensible and easier to maintain
And as a bonus we can take our code to other platforms
Creating a Reactive Microservice
Prerequisites
- JDK 1.8+
- MongoDB 3.4+ (running on localhost:27017)
- Any IDE such: Intellij IDEA Community Edition
Initialize some data
customers.json
{ "_id": 1, "name": "Kotlin"}
{ "_id": 2, "name": "Reactive"}
{ "_id": 3, "name": "Microservices" }
run from command line
$ mongoimport --collection customers --drop --file customers.json
Run it
$ mvnw spring-boot:run
INFO 1792 --- [localhost:27017] org.mongodb.driver.connection : Opened connection [connectionId{localValue:1, serverValue:1}] to localhost:27017
INFO 1792 --- [ main] o.s.b.web.embedded.netty.NettyWebServer : Netty started on port(s): 8080
INFO 1792 --- [ main] c.j.m.R.ReactiveKotlinApplicationKt : Started ReactiveKotlinApplicationKt in 3.351 seconds (JVM running for 3.721)
Customer Class
package com.juan.medina.ReactiveKotlin
data class Customer(var id : Int = 0, var name : String = "")
Creating a repository
@Repository
class CustomerRepository(private val template: ReactiveMongoTemplate) {
fun findById(id: Int) = template.findById<Customer>(id)
fun getAll() = template.findAll<Customer>()
}
Mono vs Flux
fun findById(id: Int) = template.findById<Customer>(id)
fun getAll() = template.findAll<Customer>()
template.findbyId returns a Mono<Customer>
template.findAll returns a Flux<Customer>
Mono is a promise of a value, that when is ready will be published to who is subscribe to that Mono. Flux is a promise of multiple values
Creating a Handler
@Component
class CustomerHandler(val customerRepository: CustomerRepository) {
fun getCustomer(serverRequest: ServerRequest) = ServerResponse.ok()
.body<Customer>(customerRepository.findById(serverRequest.pathVariable("id").toInt()))
fun getAllCustomers(serverRequest: ServerRequest) = ServerResponse.ok()
.body<Customer>(customerRepository.getAll())
}
Everything is reactive
ServerResponse.ok().body<Customer>(Mono<Customer>)
ServerResponse.ok().body<Customer>(Flux<Customer>)
ServerResponse.ok().body returns a Mono<ServerResponse>
that Mono<ServerResponse> is subscribed to a Mono<Customer> or Flux<Customer>
WebFlux will be subscribed to the Mono<ServerResponse>, a promise of a response that is subscribed to a promise of results of the database
Creating a Router
@Component
class CustomerRouter(val customerHandler: CustomerHandler) {
@Bean
fun customerRoutes() = router {
"/customer".nest {
GET("/{id}", customerHandler::getCustomer)
GET("/", customerHandler::getAllCustomers)
}
}
}