Kotlin. Java 대비 장점이 무엇인가?

Java도 버전 올라가면서 """ string, when expression, utility stream API 등 코틀린을 쓰면서 느꼈던 장점들이 보완되고 있다.

그렇다면 코틀린을 써야 할 이유가 있는가?

  1. 코틀린과 Java의 가장 큰 차이는, 자바는 모든 타입이 Nullable이라는 명확한 한계를 가지고 있다는 점이다. Optional이 있지만 제한적이다. 컴파일 타임에 아예 널이 될 수 없음을 강제하는 코틀린의 컨셉과는 확실한 수준 차이가 있다.
  2. Coroutine이라는 더 나은 동시성 처리 방법을 지원한다. java도 많이 편해졌지만, 결국 Mono, Flux. reactive까지다
  3. 마지막으로 Java가 많이 좋아졌다고는 하지만 아직도 default arg named param 같은 소소한 기능에서, 언어차원에서의 지원하지 않아 builder를 거쳐야 하는 등 지원이 미흡한 부분이 있다. 개발 하다 보면 소소하게 코틀린이 낫다고 생각되는 부분이 분명 존재한다.

 


 

  • Named parameter를 지원한다. 단, 자바로 작성한 코드를 호출할 때는 사용할 수 없다.
  • Default argument를 지원한다. 단, 자바에서 코틀린 코드를 호출할 때는 동작하지 않기 때문에 모든 params를 명시해주거나, ``kt @JvmOverloads`` 애노테이션으로 해결할 수 있다.
  • ``kt val``은 ``kt init``에서도 초기화 할 수 있으니 꼭 선언하면서 동시에 초기화 할 필요는 없다. ``kt init``에 적기 전까지는 IDE에서 빨간 밑줄 처리하긴 한다.
  • 하지만 ``kt var``은 private이거나 final인 경우에만 init block에서 초기화가 가능하다.(또는 lateinit 써주거나)
    • final 이어야만 init에서 초기화 가능하다는 것은, init에서 호출하면 setter가 호출될텐데 하위 클래스에서 setter가 override되어 의도와 다르게 동작하는 경우를 방지하기 위한 것으로 보임.
    • kotlin에서 final 키워드는 상속을 금지한다는 의미로, 자바의 final과 다른 의미다.
    • 원래 Kotlin에서는 필드는 기본이 final이지만, Spring Bean annotation 붙여주는 경우 해당 클래스가 allopen 될 수 있음. (allopen plugin)

 

애너테이션 

주생성자에 애너테이션 붙이는 경우, 애너테이션 타겟에 `` FIELD, PARAMETER``가 둘 다 존재하는 경우라면

필드에 붙이는건지 생성자 파라미터에 붙이는건지 애매할 수 있다.

(Byte code 변환 후 Java로 Decompile 해보면 알 수 있음)

필드에 붙이려면 @field: 명시해준다.

```kt

@field:Positive

```

 

숫자 타입은 가독성을 위해 언더바 를 넣을 수 있음

```kt

val krw = 2_000_000

```

 

""" 들여쓰기 제거

```kt
println("""
    aaa
    bbb""".trimIndent())
```
 

!in

데이터가 집단에 속하는지는 ``kt in``으로 검사. 부정은 ``kt !in``

```kt

fun isNotDigit(c: Char) = c !in '0'..'9'

isNotDigit('a')

```

 

loop , 반복

```kt
for (i in 0..10)          // 0 1 2 3 4 5 6 7 8 9 10
for (i in 0 until  10)    // 0 1 2 3 4 5 6 7 8 9    == python range(10)
for (i in 10 downTo 0 step 2)     // 10 8 6 4 2 0
for (c in 'A'..'F')       // ABCDEF
```
 
python의 ``py enumerate``같은 기능은 ``kt .withIndex()``다.
구조 분해 선언(destructuring declaration)이 가능하기 때문에 다음과 같이 쓸 수 있다.
```kt
val list = 'A'..'F'
for ((i, c) in list.withIndex()){
    println("test $ $")
}
```
 
단순 for 말고 repeat() 도 괜찮음
```kt
repeat(times = repeatMax - 1) {
    ...
}
 
```
 

iterator가 아닌 타입을 iterable로 : asIterable()

List로 변환해서 이걸 써도 되기는 하는데, asIterable이라는 유틸리티가 있긴 하다.
* forEach도 있어서 이걸 써도 된다.
```kt
for (repo in repos!!.asIterable()) {
    Log.d(DBG_TAG, repo.toString())
}
```
 
 

상수 : 최상위 프로퍼티

최상위 프로퍼티도 접근자 메소드가 생성되고, 이를 통해 접근하게 되기 때문에 상수처럼 쓰고 싶은 경우 자연스럽지 못하다. 이런 경우 ``kt const``를 붙이면 ``java public static final``로 지정된다.
```kt
const val COUNT = 5
```
 

inline 사용 예

그냥 코드에 매번 ``kt if``로 검사
-> ``kt if`` 검사를 메소드로 뺌(``kt isXXX()``)
-> 이를 ``kt inline``으로 만들어서, 매 코드마다 ``kt if``로 검사하는 것과 동일한 효과를 가져온다.
```kt
inline fun ifConThenRun(code: () -> Unit) {
    if (SomeCondition) {
        code()
    }
}
>>> ifConThenRun { userCode() }
```
* 람다를 인자로 받을 때는 이런 식으로 적는다.
* ``kt Unit``말고 다른 타입을 지정해도 된다. 람다 자체는 뭘 리턴하든 별 상관이 없기 때문. 중요한건 함수 전체의 리턴값이다.
 

buildString : 문자열 만들기 제일 간단하게 처리하기

``kt StringBuilder()``를 만들고, ``kt .toString()``을 반환하는 것을 자동으로 처리해주는 ``kt buildString``이 있다.

```kt

for alphabet() = buildString {

    for (letter in 'A'..'Z') {

        append(letter)

    }

}

```