4. Combines OO and functional features
Focused on
• Interoperability
• Safety
• Clarity
• Tooling support
a pragmatic programming language
for JVM, Android, JavaScript, LLVM
7. Concise
Drastically reduce
The amount of boilerplate code you need to write
data class Customer(val name: String,
var email: String,
val company: String = “coupang”)
Use lambda expression
val positiveNumbers = list.filter { it > 0 }
data class : POJO with getter,setter, equals, hashCode, toString, copy
8. Concise
Use lambda expression
val positiveNumbers = list.firstOrNull { it > 0 }
Lazy initialization
lateinit var positiveNumbers:List<Int>
val positiveNumbers by lazy { listOf(1,2,3) }
9. SAFE
All type is Nonnull is default
var output:String
output = null // compilation error
println(output.length())
user?.name?.isEmpty() ?: true
Rid NullPointerExceptions !!!
Auto-cast
fun calcuateTotal(obj:Any) {
if(obj is Invoice) {
obj.calculateTotal() // auto-cast
}
}
10. Versatile
• Android : No performance impact. Very small runtime.
• JavaScript: Write code in Kotlin and target JavaScript to run on Node.js or in
browser
• Web: Whether you want strongly typed HTML, CSS builder or just plain web
development
• Application Server
• The technology doesn’t matter. 100% compatible with all JVM frameworks.
• Enterpise
• Use Kotlin for any type of Enterprise Java EE development.
• Native
• Kotlin/Native is a LLVM backend for the Kotlin compiler, runtime implementation
and native code generation facility using LLVM toolchain.
12. Functions
fun sum(a: Int, b: Int): Int {
return a + b
}
fun sum(a: Int, b: Int) = a + b
fun printSum(a: Int, b: Int): Unit {
print(a + b)
}
Variables
val a: Int = 1
val b = 1 // `Int` type is inferred
var c: Int // Type required when no initializer
// is provided
c = 1 // definite assignment
var x = 5 // `Int` type is inferred
x += 1
x ++
13. String Interpolation
val i = 10
val s = “i=$i” // evaluates to “i=10"
val s = “abc”
val str = “$s.length is ${s.length}”
// evaluates to “abc.length is 3"
val price = ""”
|${‘$’}9.99
""”.trimMargin()
When Expression
when (x) {
1 -> print(“x == 1”)
2 -> print(“x == 2”)
0, 1 -> print(“x == 0 or x == 1”)
parseInt(s) -> print(“s encodes x”)
in 1..10 -> print(”x is in the range”)
in validNumbers ->
!in 10..20 -> print(”x is not in the range”)
is String -> x.startsWith(“prefix”)
x.isOdd() -> print(“x is odd”)
x.isEven() -> print(“x is even”)
else -> println(“otherwise”)
}
14. Loop
for(item in collection)
print(item)
for(i in array.indices)
print(array[i])
for((index,value) in array.withIndex()) {
print(“element at $index is $value”)
}
Collections
val immutableList = listOf(1, 2, 3)
val mutable = mutableListOf(1, 2, 3)
val arrayList = arrayListOf(1, 2, 3)
val immutableMap = mapOf("foo" to 1, "bar" to 2)
val readWriteMap = hashMapOf("foo" to 1, "bar" to 2)
println(map["foo"])
20. A Fiber is a lightweight thread that uses cooperative multitasking instead of preemptive
multitasking. A running fiber must explicitly "yield" to allow another fiber to run, which makes their
implementation much easier than kernel or user threads.
A Coroutine is a component that generalizes a subroutine to allow multiple entry points for
suspending and resuming execution at certain locations. Unlike subroutines, coroutines can
exit by calling other coroutines, which may later return to the point where they were invoked in the
original coroutine.
A Green Thread is a thread that is scheduled by a virtual machine (VM) instead of natively by the
underlying operating system. Green threads emulate multithreaded environments without relying on
any native OS capabilities, and they are managed in user space instead of kernel space, enabling
them to work in environments that do not have native thread support.
21. • Process: OS-managed (possibly) truly concurrent, at least in the presence of suitable hardware
support. Exist within their own address space.
• Thread: OS-managed, within the same address space as the parent and all its other threads.
Possibly truly concurrent, and multi-tasking is pre-emptive.
• Green Thread: These are user-space projections of the same concept as threads, but are not OS-
managed. Probably not truly concurrent, except in the sense that there may be multiple worker
threads or processes giving them CPU time concurrently, so probably best to consider this as
interleaved or multiplexed.
• Fibers: OS-managed. Exactly threads, except co-operatively multitasking, and hence not truly
concurrent.
• Coroutines: Exactly fibers, except not OS-managed.
• Goroutines: They claim to be unlike anything else, but they seem to be exactly green threads, as
in, process-managed in a single address space and multiplexed onto system threads. Perhaps
somebody with more knowledge of Go can cut through the marketing material.
25. Coroutines Goals
• Asynchronous programming (and more)
• without explicit callbacks
• without explicit Future combinators (see Deathwing code)
• Maximum flexibility for library designer
• with minimal runtime support
• and no macros (like Scala)
26. Coroutines 종류
Stackless Stackful
Language
restrictions
Use in special context 😫 Use anywhere 😀
Implemented in C#, Scala, Kotlin, … Quasar, Javaflow …
Code transformation Local (compiler magic) 😀 All over the place 😫
Runtime support Little 😀 Substantial 😫
27. The C# Way
async Task<String> work() {
Thread.sleep(200);
return “done”;
}
async Task moreWork() {
Console.WriteLine(“Work started”);
var str = await work();
Console.WriteLine($“Work completed: {str}”);
}
28. The Kotlin Way
fun work(): Deferred<String> = async(CommonPool) {
delay(200). // non-blocking Thread.sleep()
“done”
}
fun moreWork():Deferred<Unit> = async(CommonPool) {
println(“Work started”)
val str = await(work())
println(“Work completed: $str”)
}
29. How suspension works
fun moreWork():Deferred<Unit> =
async(CommonPool) {
println(“Work started”)
val str = await(work())
println(“Work completed: $str”)
}
controller.await(
work(),
current_continuation
)
return
30. Coroutines : Job
@Test fun `coroutine is light-weight`() = runBlocking {
val jobs = List(100_000) {
launch(CommonPool) {
delay(1000L)
print(".")
}
}
jobs.forEach { it.join() }
}
31. Yield example : Lazy Fibonacci
val seq = buildSequence<Int> {
var a = 0
var b = 1
yield(1)
while (true) {
yield(a + b)
val tmp = a + b
a = b
b = tmp
}
}
val list = seq.take(10).toList()
assertThat(list).isEqualTo(listOf(1, 1, 2, 3, 5, 8, 13, 21, 34, 55))
38. AsyncRestTemplate
• Spring 4.0 부터 RestTemplate 의 비동기-논블록킹 버전인
AsyncRestTemplate 제공
• 겉은 비동기-논블록킹이지만 논블록킹 IO 를 사용하지 않음 ㅠ.
ㅠ
• Netty 를 이용하여 논블록킹 IO (NioEventLoopEventGroup) 을
사용할 수 있음
• 리턴 타입이 ListenableFuture 라 콜백 헬 발생 !!!
ListenableFuture 를 Kotlin Coroutines 의 Deferred 로 변환
가능
39. Async@Spring
• TaskExecutor 의 전략적 활용이 중요
• 스프링의 모든 비동기 기술에는 ExecutorService 의 세밀한 설정이 가
능
• CompletableFuture 도 ExecutorService의 설계가 중요
• 비동기 스프링 기술을 사용하는 이유?
• IO 가 많은 서버에서 서버 자원의 효율적 활용
Low Latency, High Throughput
• 서버 외부 작업
• 그럼 Spring 에서 다른 방법은 없나
• Callback Hell 은 못 피하는 것인가?
• Java 8 ForkJoin Pool 이나 Scala 의 Global Executor 을 쓰면 안되나?
•
41. Coroutines with Spring
• Kotlin suspend 함수를 Spring 에서 직접 이용
• Spring TaskExecutor 를 사용하지 않고, ForkJoin Pool 사용
• Callback Hell 없이 async/await 사용
• Spring 에서 Kotlin Coroutines 의 다양한 기능 활용
• Async IO
• Channel
42. Coroutines 을 사용하기 위한 Spring Configuration
@SpringBootApplication
@EnableAutoConfiguration
@EnableCoroutine
@EnableWebCoroutine
open class DemoAppConfiguration {
@Bean
fun demoService(): DemoService {
return DemoService()
}
}
43. open class DemoService {
@Scheduled(fixedRate = 60_000)
suspend open fun everyMinute() {
log.info { "I'm still alive ... every minutes I will back..." }
}
suspend open fun delayedReturn(str: String, delayMillis: Long): String {
delay(delayMillis)
return str
}
@Coroutine(COMMON_POOL)
suspend open fun commonPoolReturn(str: String, delayMillis: Long): String {
delay(delayMillis)
return str
}
}
44. Coroutines 가 적용된 RestController
@RestController
open class DemoController @Inject constructor(private val demoService: DemoService) {
private val restOperations = CoroutineRestOperations.invoke()
@GetMapping("/delayed")
suspend open fun delayedReturn(): String {
log.info { "Before call to [demoService.delayed]" }
val result = demoService.delayedReturn("delayed", 1000)
log.info { "After call to [demoService.delayed]" }
return result
}
@GetMapping("/commonPool")
suspend open fun commonPoolReturn(): String {
log.info { "Before call to [demoService.commonPoolReturn]" }
val result = demoService.commonPoolReturn("commonPool", 1000)
log.info { "After call to [demoService.commonPoolReturn]" }
return result
}
45. @GetMapping("/rest")
@Coroutine(COMMON_POOL)
suspend open fun rest(request: HttpServletRequest): String {
val result = restOperations.getForEntity(request.requestURL.toString()
.replace("rest", "delayed"),
String::class.java)
return "Rest result: ${result.body}"
}
RestTemplate 에서 Coroutine 이 적용된 CoroutineRestOperations 인터페이스를 사용하여,
외부 서버 정보를 요청할 수도 있다. RestTemplate 처럼 Blocking 되지 않는다.
interface CoroutineRestOperations {
companion object : KLogging() {
operator fun invoke(restOperations: RestOperations = RestTemplate(),
context: CoroutineContext = NewThreadCoroutineDispatcher): CoroutineRestOperations {
log.info { "Create CoroutineRestOperations Proxy ..." }
return createCoroutineProxy(CoroutineRestOperations::class.java,
restOperations,
DefaultCoroutineProxyConfig(context))
}
}
}
suspend fun <T : Any?> getForEntity(url: String, responseType: Class<T>?, vararg uriVariables: Any?):
ResponseEntity<T>
46. Concolusion
• 범용의 Spring Framework 에 Kotlin Coroutines 을 사용
• Low latency, High throughput 실현
• 비동기, 논 블럭킹을 Readable Code 로 구현
• Java CompletableFuture 의 thenXXXX 메소드에서 해방
• Spring context 를 이용하여 향후 retry, scheduled 등 다양한
방식의 구현체에 Coroutines 적용 예정
• Effective Java 를 배워서 구현하려면, 차라리 Kotlin 으로 구현
해라
• 향후 Kotlin Native 로 진정한 비동기 IO 를 사용할 수 있다.