Generics

다음 내용을 정리한다.

  • 제네릭 타입 파라미터
  • 제네릭 함수
  • 제네릭 클래스
  • 제네릭 타입 파라미터 제약
  • 제네릭스 동작 원리

제네릭 타입 파마리터

제네릭스를 사용하면, 타입 파마리터를 받는 타입을 정의할 수 있다.
제네릭 타입의 인스턴스를 만들려면, 타입 파라미터를 구체적인 타입 파라미터로 치환해야한다.

예를 들어, Map 클래스는 키 타입과 값 타입을 타입 파리미터로 받으므로 Map<K, V> 이다.
이 제네릭 클래스에 대해 제네릭 타입의 인스턴스를 만들려면,
Map<String, Person> 처럼 구체적인 타입 파라미터로 치환해서 인스턴스화할 수 있다.

타입 파라미터 추론

코틀린 컴파일러는 보통의 타입과 마찬가지로, 타입 파라미터도 추론가능하다.

코틀린 Collections 의 listOf function 에는 아래 처럼, 타입 파라미터 T 가 정의되어 있다.

아래에서는, listOf 에 전달된 두 값이 문자열이므로 여기서 생기는 리스트가 List<String> 임을 추론한다.

Read more

Spring WebFlux Thread Model

Spring Webflux 에서는 어떤 thread 들이 어떻게 request 를 처리하는지 정리한다.

Threads: reactor-http-nio

1
2
3
4
On a “vanilla” Spring WebFlux server (for example, no data access nor other optional dependencies), 
you can expect one thread for the server and several others for request processing (typically as many as the number of CPU cores).
Servlet containers, however, may start with more threads (for example, 10 on Tomcat),
in support of both servlet (blocking) I/O and servlet 3.1 (non-blocking) I/O usage.

Spring 공식 문서에 따르면,
부가적인 dependency 가 없는 Spring WebFlux 서버는,

  1. 서버를 위한 thread 하나와
  2. 요청 처리를 위한 여러 thread 들로 구성된다.

spring-boot-starter-webflux dependency 만 추가해서 project 하나를 만들어보자.

그리고, 아래와 같이 ‘1,2,3’ 을 응답하는 간단한 endpoint 를 추가하자.

1
2
3
4
5
6
7
8
9
10
11
12
13
@RestController
@RequestMapping
class NumbersController {

private val logger = LoggerFactory.getLogger(this::class.java)

@GetMapping("/local")
fun getNumbers(): Flux<Int> {
logger.info("-- getNumbers")

return Flux.fromIterable(listOf(1, 2, 3))
}
}

App 을 실행시키고, 해당 endpoint 를 호출해보자.

Read more

map, flatMap

Project Reactor 의 “map, flatMap” transform operator 에 대해 정리한다.

map

하난의 element 를 1-to-1 방식으로 변형한다.
공식 문서의 Mono 의 map 정의는 다음과 같다.

1
2
Transform the item emitted by this Mono 
by applying a synchronous function to it.

공식 문서의 Flux 의 map 정의는 다음과 같다.

1
2
Transform the items emitted by this Flux 
by applying a synchronous function to each item.

다응 코드를 보자.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
fun namesFluxMapAndFilter(strLen: Long): Flux<String> {
val names = listOf("ko", "jun", "hee")

return Flux.fromIterable(names)
.map { it.uppercase(Locale.getDefault()) } // HERE !!
.filter { it.length > strLen }
}

@Test
fun namesFluxMapAndFilter() {
// given
val strLen = 2L

// when
val names: Flux<String> = fluxMonoGenerator.namesFluxMapAndFilter(strLen)

// then
StepVerifier.create(names)
.expectNext("JUN", "HEE")
.verifyComplete()
}

위 코드는,

  1. 각 element 를 대문자로 변형한다. (map)
    “ko” -> “KO”
    “jun” -> “JUN”
    “hee” -> “HEE”
  2. 길이가 2 보다 큰 element 만 추출한다. (filter)
    “JUN”, “HEE”
Read more

고차함수

코틀린의 고차 함수에 대해 정리한다.

고차 함수

고차 함수란, “다른 함수를 인자로 받거나 반환하는 함수” 이다.

그런데 코틀린에서는, 람다나 함수 참조를 사용해서 함수를 값으로 표현할 수 있다.
그래서, “람다나 함수 참조를 인자로 반거나 반환하는 함수” 도 고차 함수이다.

예를 들어, 표준 라이브러리에 있는 함수인 filter 는 고차 함수이다.
filter 는 술어 함수를 인자로 받는 함수이기 때문이다.

1
2
val numbers = listOf("one", "two", "three", "four")
val longerThan3 = numbers.filter { it.length > 3 } // 술어함수를 인자로 받는 filter

함수 타입

위에서 정리한 것 처럼, 람다를 인자로 받는 함수는 고차함수이다.
그러면 람다 인자의 타입은 어떻게 선언할까 ?

더 단순한 단계인, 람다를 로컬 변수에 대입하는 경우를 보자.

1
2
val sum = { x: Int, y: Int -> x + y }
val action = { println(sum) }
Read more

Convention

코틀린의 관례에 대해 다음 순으로, 정리한다.

  1. 산술 연산자
  2. 비교 연산자
  3. 컬렉션, 범위
  4. 구조 분해 선언

관례란, 언어 기능과 미리 정해진 이름의 함수를 연결해주는 기법이다.

산술 연산자 오버로딩

자바에서는,

  1. 원시 타입에 대해서 산술 연산자를 사용할 수 있으며
  2. String 에 대해 + 연산자를 사용할 수 있다.

코틀린에서는, 다른 클래스에도 산술 연산자를 사용할 수 있다.

이항 산술 연산 오버로딩

연산자를 오버로딩하는 함수 앞에는 operator 키워드가 붙어야한다.
다음을 보자.

1
2
3
4
5
6
7
8
9
10
11
12
13
fun main() {
val p1 = Point(10, 20)
val p2 = Point(30, 40)

println(p1 + p2)
}

data class Point(val x: Int, val y: Int) {

operator fun plus(other: Point): Point {
return Point(x + other.x, y + other.y)
}
}
Read more