코틀린에서는, 어떤 변수라도 멤버 함수와 속성을 호출 할 수 있다는 점에서 모든 것은 객체로 볼 수 있다. 일부 타입은 특수 표현 내부 표현을 가질 수 있다. 예를 들어 number, character, boolean 타입은 런타임에 원시 타입 값으로 표현될 수 있지만, 사용자에게는 일반 클래스처럼 보일 수 있다.
Int의 최대 값을 초과하지 않는 정수 값으로 초기화 된 모든 변수는 Int 유형이 유추된다. 초기 값이 Int 범위를 초과하면 유형은 Long으로 선언된다. Long 값을 명시 적으로 지정하려면 값에 접미사 L을 추가하면 된다.
val one = 1 // Int
val threeBillion = 3000000000 // Long
val oneLong = 1L // Long
val oneByte: Byte = 1
부동 소수점 숫자의 경우 Kotlin은 Float 및 Double 유형을 제공한다. IEEE 754 표준에 따르면 부동 소수점 유형은 저장할 수 있는 소수점 자릿수에 따라 다르다. Float은 IEEE 754 단일 정밀도를 반영하고, Double은 두배의 정밀도를 반영한다.
분수로 초기화 된 변수의 경우 컴파일러는 Double 유형으로 유추하게 된다. 값에 대해 부동 소수점 유형을 명시적으로 지정하려면 접미사 f 또는 F를 추가하면 된다. 부동 소수점이 7자리 이상일 경우 자동으로 반올림처리 된다.
val pi = 3.14 // Double
val e = 2.7182818284 // Double
val eFloat = 2.7182818284f // Float, actual value is 2.7182817
Underscores in numeric literals
밑줄을 사용하여 숫자 상수를 더 읽기 쉽게 사용할 수 있다.
val oneMillion = 1_000_000
val creditCardNumber = 1234_5678_9012_3456L
val socialSecurityNumber = 999_99_9999L
val hexBytes = 0xFF_EC_DE_5E
val bytes = 0b11010010_01101001_10010100_10010010
Representation
Java 플랫폼에서 숫자는 nullable 숫자가 필요하지 않거나 제네릭이 관련되지 않는 한 JVM 원시 타입으로 저장된다.
val a: Int = 100
val boxedA: Int? = a
val anotherBoxedA: Int? = a
val b: Int = 10000
val boxedB: Int? = b
val anotherBoxedB: Int? = b
println(boxedA === anotherBoxedA) // true
println(boxedB === anotherBoxedB) // false
Operations
Kotlin은 산술연산을 지원하지만 컴파일러는 해당 명령어에 따라 호출을 최적화하기 때문에 주의해야 한다.
Division of integers
// case 1. 정수 간 나누기는 항상 정수를 반환한다.
val x1 = 5 / 2
//println(x == 2.5) // ERROR: Operator '==' cannot be applied to 'Int' and 'Double'
println(x1 == 2) // true
// case 2. 이는 두 정수 유형 사이의 분할에 해당된다.
val x2 = 5L / 2
println(x2 == 2L) // true
// case 3. 부동 소수점 유형을 반환하려면 인수 중 하나를 부동 소수점 유형으로 명시적으로 변환한다.
val x3 = 5 / 2.toDouble()
println(x3 == 2.5) // true
Strings
문자열은 변경할 수 없으며 문자열의 요소는 인덱싱 작업으로 엑세스 할 수 있다.
for (c in str) {
println(c)
}
String literals
코틀린에는 두 가지 타입의 문자열 리터럴을 가질 수 있다.
이스케이프된 문자열 : 이스케이프된 문자열을 포함한 문자열
단순 문자열 : 줄 바꿈이나 임의의 텍스트를 포함한 문자열
val s = "Hello, world!\\n"
단순 문자열은 삼중 따옴표로 구분되며 이스케이프를 삼중 따옴표로 구분되며 이스케이프를 포함하지 않으며 줄 바꿈 및 기타 문자를 포함할 수 있다.
val text = """
for (c in "foo")
print(c)
"""
trimMargin () 함수로 선행 공백을 제거 할 수 있다.
val text = """
|Tell me and I forget.
|Teach me and I remember.
|Involve me and I learn.
|(Benjamin Franklin)
""".trimMargin()
// result
length is 3
Tell me and I forget.
Teach me and I remember.
Involve me and I learn.
(Benjamin Franklin)
기본적으로 '|' 문자는 여백 접두사로 사용되지만 trimMargin(">")과 같은 매개 변수로 사용할 수도 있다.
val max = if (a > b) {
print("Choose a")
a
} else {
print("Choose b")
b
}
만약 if 문을 표현식 대신에 값을 할당하는 블록으로 대신할 경우 eles 브랜치는 필수로 정의해야 한다.
When Expression
when 표현식은 C 언어와 유사하다.
when (x) {
1 -> print("x == 1")
2 -> print("x == 2")
else -> { // Note the block
print("x is neither 1 nor 2")
}
}
만약 다양한 케이스가 동일한 방식으로 처리된다면 콤마로 구분하여 중복 사용할 수 있다.
when (x) {
0, 1 -> print("x == 0 or x == 1")
else -> print("otherwise")
}
조건문에 범위를 사용할 수 있다.
when (x) {
in 1..10 -> print("x is in the range")
in validNumbers -> print("x is valid")
!in 10..20 -> print("x is outside the range")
else -> print("none of the above")
}
또 다른 사용법으로는 특정 타입을 is 또는 !is 를 사용하여 구분할 수 있다.
fun hasPrefix(x: Any) = when(x) {
is String -> x.startsWith("prefix")
else -> false
}
For Loops
for 루프는 반복자를 제공하는 모든 항목을 반복할 수 있다. 이는 C#과 같은 언어의 foreach 루프와 동일하다.
// case 1. 단순 for loop
for (item in collection) print(item)
// case 2. loop 내에 블록문이 필요할 경우
for (item: Int in ints) {
// ...
}
만약 Array를 루프돌 경우에는 인덱스가 필요할 수 있다.
// case 1. indices를 사용하여 인덱스를 추출한다.
for (i in array.indices) {
println(array[i])
}
// case 2. withIndex 메소드를 사용하여 인덱스와 값을 추출할 수 있다.
for ((index, value) in array.withIndex()) {
println("the element at $index is $value")
}
While Loops
이는 Java와 동일하다
while (x > 0) {
x--
}
do {
val y = retrieveData()
} while (y != null) // y is visible here!
Kotlin의 모든 표현식은 라벨로 표시 될 수 있다. break 또는 continue에 라벨을 함께 사용하면 된다.
loop@ for (i in 1..100) {
for (j in 1..100) {
if (...) break@loop
}
}
Return at Labels
return 키워드를 사용하면 가장 근접한 메소드를 종료 한다.
fun foo() {
listOf(1, 2, 3, 4, 5).forEach {
if (it == 3) return // non-local return directly to the caller of foo()
print(it)
}
println("this point is unreachable")
}
// result
12
람다식에서 반환해야 하는 경우 라벨을 지정하고 반환을 한정할 수 있다.
fun foo() {
listOf(1, 2, 3, 4, 5).forEach lit@{
if (it == 3) return@lit // local return to the caller of the lambda, i.e. the forEach loop
print(it)
}
print(" done with explicit label")
}
// result
// 1245 done with explicit label
라벨을 사용하지 않아도 인접한 루프문으로 암시적으로 사용할 수 있다.
fun foo() {
listOf(1, 2, 3, 4, 5).forEach {
if (it == 3) return@forEach // local return to the caller of the lambda, i.e. the forEach loop
print(it)
}
print(" done with implicit label")
}
// result
// 1245 done with implicit label
익명 함수의 return 문은 익명 함수 자체에서 반환한다.
fun foo() {
listOf(1, 2, 3, 4, 5).forEach(fun(value: Int) {
if (value == 3) return // local return to the caller of the anonymous fun, i.e. the forEach loop
print(value)
})
print(" done with anonymous function")
}
// result
// 1245 done with implicit label
앞의 세 가지 예에서 로컬 리턴을 사용하는 것은 일반 루프에서 continue를 사용하는 것과 유사하다. break에 직접 해당하는 것은 없지만 라벨을 사용하여 유사하게 사용할 수 있다.
fun foo() {
run loop@{
listOf(1, 2, 3, 4, 5).forEach {
if (it == 3) return@loop // non-local return from the lambda passed to run
print(it)
}
}
print(" done with nested loop")
}
// result
// 12 done with nested loop