코틀린 기본 문법
주석
자바와 마찬가지로 코틀린은 세 가지 주석을 지원하며, 코드를 문서화할 때 사용합니다.
- 한 줄 짜리 주석: //로 시작하고 줄이 끝나면 주석도 끝난다.
- 여러 줄 주석: /*로 시작하고 */로 끝난다.
- KDoc 여러 줄 주석: /**로 시작하고 */로 끝난다.
- Javadoc과 비슷한 리치 텍스트 문서를 생성하기 위해 사용
자바와 달리 코틀린에서는 여러 줄 주석으로 여러번 내포시킬 수 있다.
불변(Immutable) 변수
코틀린에서 변수를 정의하는 가장 간단한 형태는 다음과 같습니다.
val num = 15
- val 키워드: 값을 뜻하는 value에서 유래
- 변수 식별자: 새 변수에 이름을 부여
- 변수의 초깃값: = 기호 뒤에 옴
val 변수는 불변 변수로 한 번 초기화하면 다시는 값을 대입할 수 없는 변수로, 자바의 final과 비슷한 변수입니다.
불변 변수를 사용하면 함수가 부수 효과를 일으키지 못하고 함수형 스타일 코드를 장려할 수 있습니다.
자바와 달리 코틀린에는 세미콜론(;)을 생략해도 됩니다. 실제로 세미콜론을 쓰지 않는 스타일을 권장합니다.
한 줄에 한 문장만 넣으면 실질적으로 코드에서 세미콜론을 거의 사용하지 않아도 됩니다.
코틀린에서는 타입 추론(type inference) 기능으로 인해 따로 변수 타입을 지정하지 않아도 프로그램이 컴파일되고 실행됩니다. 이러한 타입 추론 덕분에 코틀린은 강한 타입 지정 언어이자, 사용자가 불필요한 타입 정보를 코드에 추가해 코드가 지저분해지는 일을 막아줍니다.
JAVA10에서부터는 코틀린과 비슷한 지역 변수 타입 추론을 도입했습니다. 하지만, 코틀린은 지역 변수 뿐 아니라 더 넓은 요소에 대해 타입 추론을 해줍니다.
만약 타입을 명시적으로 표시해야 한다면, 아래와 같이 지정할 수 있습니다.
val n: Int = 100
// 초기값을 생략하고 나중에 다른 문(statement)에서 변수를 초기화할 때에는 아래와 같이 사용
val num: Int
n = 10
자바와 달리 코틀린 식별자에는 달러 기호($)를 사용할 수 없습니다.
가변(mutable) 변수
var sum = 1
sum = sum + 2
가변 변수에 처음 값을 대입할 때 추론된 변수 타입은 변수가 불변이든 그렇지 않든 계속 유지되므로, 이후 값을 대입할 때 다른 타입의 값을 대입하면 컴파일 오류가 발생합니다.
자바와 달리 코틀린 대입은 문(statement)입니다.
따라서, 아무 값도 돌려주지 않아 a = b = c와 같은 대입문 연쇄를 사용할 수 없습니다.
문(statement) VS 식(expression)
- 문(statement): 값을 리턴해주지 않음
- 식(expression): 값을 리턴해줌
기본 타입
자바에서는 int와 같은 원시 타입과 String과 같은 클래스를 기반으로 한 참조 타입 사이에 명확한 구분이 있었습니다. 코틀린에서는 똑같은 타입(예: Int)이 문맥에 따라 원시 타입과 참조 타입을 가리키기 때문에 이런 구분이 약간 모호합니다.
- 자바는 원시 타입을 감싸는 특별한 박싱 타입이 있지만, 코틀린은 필요할 때 암시적으로 박싱을 수행합니다.
자바와 달리 코틀린 타입은 근본적으로 어떤 클래스 정의를 기반으로 만들어집니다. 즉, Int와 같이 원시 타입과 비슷한 타입들도 메서드와 프로퍼티를 제공합니다.
정수 타입
각 정수 타입에는 최솟값(MIN_VALUE)과 최댓값(MAX_VALUE)을 포함하는 상수 정의가 들어있습니다. 이런 상수를 사용하고자 한다면 아래와 같이 타입 이름을 붙여야 합니다.
Short.MIN_VALUE // -32768
Short.MAX_VALUE // 32167
- 각 수 타입마다 값을 다른 수 타입으로 변환하는 연ㅅ나지 정의되어 있습니다. 예를 들어 toByte(), toShort(), toInt()와 같이 변환하려는 목적 타입을 알기 쉬운 이름으로 되어 있습니다.
자바와 달리 코틀린에서는 범위가 큰 타입을 사용해야 하는 문맥에 범위가 작은 타입을 사용할 수 없습니다.
예를 들어, Int를 Long에 대입할 수 없습니다.
비교와 동등성
일반적으로 동등성 연산인 ==와 !=를 모든 타입의 값에 적용할 수 있습니다. 하지만, 수 타입이나 Char과 Boolean의 경우 예외가 있습니다.
val a = 1 // Int
val b = 2L // Long
println(a == b) // Error: comparing Int and Long
println(a.toLong() == b) // false
값이 박싱되어 있는지에 따라 동등성 연산이 다른 결과를 나을 수 있어, 코틀린 타입은 두 인자가 모두 같은 타입일 때만 ==와 !=을 허용합니다. 다만, 모든 수 타입의 값은 서로 <, <=, >, >=을 사용해 비교가 가능합니다.
- 코틀린의 박싱은 암시적으로 진행되어 타입 사이 동등성 연산을 허용하면 혼란을 야기할 수 있음
문자열
자바와 마찬가지로 코틀린 문자열도 불변입니다. 따라서, String 객체를 만들고 나면 그 안의 문자를 변경할 수 없고 문자열을 읽기만 할 수 있으며, 문자를 바꾸고 싶다면 기존 문자열을 바탕으로 새로운 문자열을 만들어야 합니다.
문자열 템플릿
문자열 리터럴을 정의하는 가장 간단한 방법은 자바와 마찬가지로 큰따옴표(")로 문자열을 감싸는 것입니다.
val hello = "Hello, Kotlin!"
기본 리터럴은 기본적으로 자바 문자열과 같지만, 코틀린은 이와 더불어 여러 가지 식에서 문자열을 합성해내는 훨씬 더 강력한 방법을 지원합니다.
import java.util.Date
fun main() {
val name = "JS"
println("Hello, $name!\n Today is ${Date()}")
}
- 기본적으로, ${}의 중괄호 사이에 넣기만 하면 어떤 코틀린 식이든 문자열에 넣을 수 있습니다.
- 위 예제의 $name과 같이 간단한 변수 참조 식인 경우 중괄호를 생략하고 달러 기호만 붙여도 됩니다.
- 이와 같은 기능을 문자열 템플릿(String template)이라고 합니다.
String Template을 사용하는 것은, 사실상 StringBuilder를 통해 String을 append하는 것과 같습니다.
@Test
fun givenTwoStrings_concatenateWithTemplates_thenEquals() {
val a = "Hello"
val b = "Baeldung"
val c = "$a $b"
assertEquals("Hello Baeldung", c)
}
// 위 코드에서 c를 만들 때 Kotlin compiler는 아래와 같이 번역합니다.
new StringBuilder().append(a).append(" ").append(b).toString()
또 다른 문자열 유형으로는 로우 문자열(raw string)이 있습니다.
fun main() {
val name = "JS"
val message = """
Hello, $name
Today is ${Date()}
""".trimIndent()
println(message)
}
- 로우 문자열을 사용하면 이스케이프 시퀀스를 사용하지 않고도 문자열을 작성할 수 있습니다.
- trimIndent() 함수는 여러 줄에 공통된 최소 들여쓰기를 제거해주는 표준 코틀린 함수로 해당 함수를 사용하면 Hello와 Today 앞의 공통된 들여쓰기를 제거해줍니다.
모든 String 인스턴스는 문자열 길이를 반환해주는 length와 문자열의 마지막 문자 인덱스를 표현하는 lastIndex 프로퍼티를 제공합니다.
자바의 ==와 != 연산자는 참조 동등성(referential equality)을 비교하기 떄문에 실제 문자열을 비교하려면 equals() 메소드를 사용해야 합니다. 하지만, 코틀린은 ==가 기본적으로 equals()를 가리키는 편의 문법이기 때문에 ==를 사용하면 직접 equals()를 호출하므로 따로 equals()를 호출할 필요가 없습니다.
배열
배열 구조를 구현하는 가장 일반적인 코틀린 타입은 Array<T>입니다.
val a = emptyArray<String>()
val b = arrayOf("hello", "kotlin")
val c = arrayOf(1, 2, 3)
- 코틀린의 배열 함수는 제네릭(generic)하기 때문에, 호출할 때 원소의 타입을 지정해주어야 합니다.
- 하지만, 코틀린의 타입 추론 덕에 위 예시의 b와 c 배열은 인자들의 타입을 통해 어떤 배열인지 따로 명시적으로 나타내지 않아도 됩니다.
Array<Int>를 사용해도 배열은 제대로 작동하지만, 모든 수를 박싱하기 때문에 ByteArray, ShortArray, IntArray, LongArray, CharArray, BooleanArray 등 특화된 배열 타입을 제공합니다. JVM에서 이런 배열 타입들은 int[], boolean[] 등의 원시 타입 배열로 표현됩니다.
자바와 달리 코틀린에는 new 연산자가 없어 배열 인스턴스 생성이 일반 함수 호출처럼 보입니다.
자바에서와 마찬가지로 배열 타입의 변수 자체에는 실제 데이터에 대한 참조를 저장하기 때문에, val로 배열 변수를 선언해도 배열의 원소값은 변경이 가능합니다.
val squares = arrayOf(1, 4, 9, 16)
squares.size // 4
squares.lastIndex // 3
squares[3] // 16
squares[2] = 100 // squares: 1, 4, 100, 16
val numbers = squares
numbers[0] = 1000 // 바뀐 데이터가 squares와 numbers에 공유됨
// 만약 원본과 별도의 배열을 만들고 싶다면 copyOf() 함수를 사용해야 함
문자열과 달리 배열에 대한 ==와 != 연산자는 원소 자체를 비교하지 않고 참조를 비교합니다. 배열 내용을 비교하고자 할 때에는 contentEquals() 함수를 사용하면 됩니다.
intArrayOf(1, 2, 3) == intArrayOf(1, 2, 3) // false
intArrayOf(1, 2, 3) == contentEquals(intArrayOf(1, 2, 3)) // true
참고 자료
'PROGRAMMING LANGUAGE > KOTLIN' 카테고리의 다른 글
[Kotlin] 널 가능성 (0) | 2022.08.12 |
---|---|
[Kotlin] 클래스 기초 (0) | 2022.08.12 |
[Kotlin] 코틀린 함수 (0) | 2022.08.04 |
[Kotlin] 코틀린이란? (0) | 2022.08.04 |
[Kotlin] 코틀린에도 삼항 연산자(ternary operator)가 있나요? (0) | 2022.06.27 |