(Kotlin) Tip2 + 문자열 템플릿
가변 인수
가변 인수를 받을 때는 앞에 vararg
키워드를 붙인다.
1
2
fun listOf<T>(vararg values: T): List<T> { ... }
이미 배열에 들어있는 원소를 가변 길이 인자로 넘길 때는 spread 연산자 \*
를 붙인다.
1
2
val list = listOf("args: ", \*args)
자바 코드로 변환해보면 호출하는 측에서
SpreadBuilder
라는 객체에\*
로 지정된 인자를 모두 풀어서 추가하고, 이를 배열로 만들어 넘기게 된다.
infix(중위) 함수 호출 구문과 딕셔너리( 맵 )
함수 선언 시에 infix
를 붙이면 3.mul(10)
을 3 mul 10
으로 쓸 수 있도록 하는 건데, 가독성에 이점이 있는 듯. 맵 만들 때 쓰면 편하다. * 파이썬의 딕셔너리가 자바에서는 맵이다.
1
2
3
4
>>> val numbers = mapOf("zero" to 0, "one" to 1)
>>> println(numbers)
삼항 연산자가 없는 대신 if로 처리할 수 있다.
코틀린에서 if
는 단순한 문장(statement)이 아니라 결과를 반환하는 식(expression)이다. Note ) if when try
모두 식(expression)이다.
1
2
var k = if (a > b) a else b
스마트 캐스트 : 코틀린에서는 타입 검사와 캐스트가 한 연산자에 의해 이루어진다.
어떤 객체의 타입을 검사했고 객체가 그 타입에 속한다면 해당 타입의 메소드나 필드 등의 멤버를 별도의 캐스트 없이 사용할 수 있다.
1
2
3
if (value is String)
println(value.toUpperCase())
Note) 단, 스마트 캐스트는 변수의 타입을 검사한 다음 변경되지 않음을 보장할 수 있는 경우에만 동작한다. 따라서 val
이면서 커스텀 접근자를 사용하지 않는 경우에만 동작한다. var
이면 null check/type check 이후 바로 변수를 사용하더라도 스마트 캐스트가 동작하지 않는데, 이는 바로 다음 코드에서 이 변수를 사용하더라도, 멀티 스레딩 환경에서는 변수가 변경되지 않음을 보장할 수 없기 때문이다.
원래 널이 될 수 있는 타입(bar?
)은 .
으로 메소드를 호출할 수 없고(널 체크 포함으로 정의된 확장 함수 제외), 널이 될 수 없는 타입의 변수에 대입할 수 없는 등 제약 조건을 가지고 있다. 그러나 스마트 캐스트가 작동할 수 있는 조건을 만족하는 변수에 대해서 널 체크를 수행하고 나면컴파일러가 그 사실을 기억해두고 null
이 아님이 확실한 영역에서는 해당 값을 널이 될 수 없는 타입의 값처럼 사용할 수 있다. 한 예로 if
또는 kt ?:
에 return
이 포함되어 있으면 이후의 코드는 실행되지 않는 것이 확실하기 때문에 스마트 캐스트가 동작한다. 그러나 return
이 포함되어 있지 않으면 프로그래머의 처리에 따라 null
인 상태로 이후 코드가 실행될 수도 있기 때문에 스마트 캐스트가 동작하지 않는다.
명시적 형변환
1
2
val n = e as Num
이런 식으로 사용하면 쓸데 없는 변수를 선언하지 않아도 돼서 편리한 경우가 있다.
1
2
(mainView as RelativeLayout).gravity
try-catch-finally
try
도 식이기 때문에 try
실행 결과를 변수에 대입할 수 있다.
1
2
3
4
5
6
7
8
9
10
fun readNumber(reader: BufferedReader) {
val number = try {
Integer.parseInt(reader.readLine())
} catch (e: NumberFormatException) {
// return 여기서 return해버리면 val number 이후 문장이 실행이 안된다.
null
}
println(number)
}
문자열 템플릿 ( string template )
자바의 문자열 접합 연산( +
) 와 동일한 기능이다.
1
2
"Hello, $"
{}
내부는 문장이 아니라 식(expression)이다. 그래서 {}
내부에 다른 식과 문, "
, 또 다른 문자열 템플릿을 적어줄 수 있다.
1
2
"Hello, $!"
변수 이름만 사용하는 경우 {}
로 감싸지 않고 바로 $name
으로 사용할 수 있지만, 이런 경우에도 {}
로 감싸주는게 좋다. 정규식 패턴 매칭이 용이하며 가독성에도 도움이 되기 때문.
- 유니코드 입력은
"\u2103"
expression body : 한 줄 함수
1
2
fun max(a: Int, b: Int): = if (a > b) a else b
이렇게 한 줄로 작성된 함수를 식이 본문인 함수(expression body)라고 부른다. 반대는 블록이 본문인 함수(block body). expression body에 =
를 사용하는 것은 함수형 개념을 잘 표현하기 때문인 것 같다.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
fun expressionBody(i: Int) = when (i%2) {
0 -> "i is $"
else -> {
println("else")
"i is odd"
}
}
fun blockBody(i: Int): String {
when (i%2) {
0 -> return "i is $"
else -> {
println("else")
return "i is odd"
}
}
}
- expression body는 알아서 타입 추론이 들어가기 때문에 리턴 타입을 지정하지 않아도 된다.
- expression body로 작성하는 경우
return
을 적어서는 안된다. - expression body 내부에서 다시
{}
를 사용하는 경우, 블록의 맨 마지막 식이 반환값이 된다.
Note ) 블록의 마지막 식이 블록의 결과라는 규칙은 블록이 값을 만들어내야 하는 경우 항상 성립한다.
- block body로 작성하는 경우 반드시
return
문이 필요하다. - block body로 작성했는데
return
을 빼먹는 경우kotlin.Unit
을 리턴하게 된다.
비트 연산자
Int, Long
타입에만 사용할 수 있다.
1
2
3
4
5
6
7
8
x.shl(y) : <<
x.shr(y) : >>
x.ushr(y) : >>>
x.and(y) : &
x.or(y) : |
x.xor(y) : ~
x.inv() : !