기본 문법
주석
- 한 줄 짜리 주석 :
//
로 시작하며 줄이 끝나면 주석도 끝난다. - 여러 줄 주석 :
/*
로 시작하고*/
로 끝난다. - KDoc 여러 줄 주석 :
/**
로 시작하고*/
로 끝난다.
자바와 달리 코틀린에서는 여러 줄 주석을 여러 번 내포시킬 수 있다.
/*
* 여러 줄 주석
/* 주석 안에 내포된 주석 */
*/
println("Hello") // 한 줄 주석
변수 정의
val timeInSecond = 15
- val 키워드 : 값을 뜻하는 value 에서 유래했다.
- 변수 식별자 : 새 변수에 이름을 부여하고 나중에 이를 가리킬 때 사용한다.
- 변수의 초깃값 :
=
기호 뒤에 온다.
코틀린에서는 줄 끝에 세미콜론을 생략해도 된다.
위 코드는 변수 타입을 지정하지 않았는데도 프로그램이 성공적으로 컴파일되고 실행된다. 코틀린의 타입 추론 기능 때문이다.
필요할 때는 타입을 명시해도 된다. 타입을 명시하려면 변수 이름 뒤에 :
을 표시하고 그 뒤에 타입을 적어주면된다.
val n :Int = 100
val text :String = "Hello"
식별자
식별자는 변수나 함수 등 프로그램에 정의된 대상에 붙은 이름이다.
코틀린 식별자는 아래 두 가지로 구분된다.
임의의 문자열
- 식별자는 오직 문자, 숫자, 밑줄 문자(
_
)만 포함한다. - 숫자로 식별자를 시작할 수는 없다.
- 밑줄로만 이뤄질 수도 있으나, 이런 식별자(
_
,__
,___
등)는 모두 미리 예약된 식별자이므로 일반적인 식별자로는 사용될 수 없다. - 하드 키워드(
val
,fun
등)를 식별자로 쓸 수는 없다.
작은역따옴표(`
)로 감싼 식별자
- 작은역따옴표(
`
) 사이에는 빈 문자열을 제외한 아무 문자열이나 와도 된다.
val `fun` = 1
val `name with spaces` = 2
가변 변수
불변 변수는 한번 초기화하면 다시는 값을 대입할 수 없는 변수이다.
불변 변수를 사용하면 함수가 사이드 이팩트를 일으키지 못하고, 함수형 스타일 코드를 장려할 수 있으며, 코드에 대한 추론이 쉬워지기 때문에 가능하면 불변 변수를 많이 사용해야 한다.
val
대신 var
키워드를 사용해 가변 변수를 정의할 수 있다.
가변 변수의 기본적인 문법은 불변 변수와 동일하나, 원할 때 변수의 값을 변경할 수 있다.
var sum = 1
sum = sum + 2
sum += 3
처음 변수에 값을 대입할 때 추론된 변수 타입은 변수가 불변이든 아니든 계속 유지되며, 잘못된 타입의 값을 대입하면 컴파일 오류가 발생한다.
자바와 달리 코틀린의 대입은 문(statement)이다. 때문에 코틀린에서는 자바의 a = b = c
와 같은 대입문 연쇄를 사용할 수 없다.
기본 타입
자바에서는 int
와 같은 원시 타입과 String
과 같이 클래스를 기반으로 하는 참조 타입 사이에 명확한 구분이 있었다.
코틀린에서는 똑같은 타입이 문맥에 따라 원시 타입과 참조 타입을 가리키기 때문에 이런 구분이 약간 모호하다.
자바에서는 원시 타입을 감싸는 특별한 박싱 타입이 있지만, 코틀린은 필요할 때 암시적으로 박싱을 수행한다.
자바와 달리 모든 코틀린 타입은 근본적으로 어떤 클래스 정의를 기반으로 만들어진다. 이 말은 Int
와 같이 원시 타입과 비슷한 타입들도 메서드와 프로퍼티를 제공한다는 뜻이다.
타입은 하위 타입(subtype)이라는 개념으로 계층화 할 수 있다. A 타입이 B 타입의 하위 타입이라는 말은 근본적으로 B 타입의 값이 쓰일 수 있는 모든 문맥에 A 타입의 값을 넣어도 아무 문제가 없다는 것이다.
예를 들어, null
을 허용하지 않는 모든 코틀린 타입은 Any
라는 내장 타입의 직간접적인 하위 타입이다. 따라서 다음 코드는 1
이라는 값을 박싱하게 만든다.
val n :Any = 1 // OK. Int는 Any의 하위 타입
정수 타입
이름 | 크기(byte) | 대응하는 자바 타입 |
---|---|---|
Byte | 1 | Byte |
Short | 2 | Short |
Int | 4 | Int |
Long | 8 | Long |
부동소수점 수
자바와 마찬가지로 코틀린도 부동소수점 수를 따르는 Float
와 Double
을 제공하며, 각각 자바의 float
와 double
에 대응한다.
비트 연산
Int
와 Long
은 비트 수준의 연산을 지원한다.
연산 | 뜻 | 해당하는 자바 연산 |
---|---|---|
shl | 왼쪽 시프트 | << |
shr | 오른쪽 시프트 | >> |
ushr | 부호 없는 오른쪽 시프트 | >>> |
and | 비트 곱(AND) | & |
or | 비트 합(OR) | | |
xor | 비트 배타합(XOR) | ^ |
inv | 비트 반전 | ~ |
수 변환
자바와 달리 코틀린에서는 범위가 큰 타입이 사용돼야 하는 문맥에 범위가 작은 타입을 쓸 수 없다.
예를 들면, Int 값을 Long 변수에 대입할 수 없다.
val n = 100
val l :Long = n // Error: can't assign Int to Long
이렇게 하게 된 이유는 암시적인 박싱 때문이다.
일반적인 Int
값이 꼭 원시 타입의 값으로 표현된다는 보장이 없다. 따라서 위 코드와 같은 더 큰 범위의 타입으로 변환하는 경우 다른 박싱한 타입의 값을 만들어낼 수 있는 가능성이 생기고, 이로 인해 동등성 요구 조건을 만족시키지 못하게 되면서 미묘한 오류를 발생시킬 수 있다.
위 코드를 올바른 코드로 인정하면 아래 연산의 결과는 false
일 것이다.
println(l == n) // Long 객체와 Int 원시 타입의 비교
하지만 이런 결과를 프로그래머가 예상하기는 힘들다. 자바에서도 박싱한 타입과 관련해 비슷한 문제가 있다.
public Integer n = 100;
public Long l = n; // Error: can't assign Integer to Long
불 타입과 논리 연산
자바와 마찬가지로 코틀린 Boolean
도 수 타입과는 다른 타입으로, 암시적으로든 toInt()
등의 명시적인 내장 연산을 써서든 수로 변환할 수 없다.
불이 지원하는 연산은 아래와 같다.
!
: 논리 부정or
,and
,xor
: 즉시 계산 방식의 논리합, 논리곱, 논리배타합||
,&&
: 지연 계산 방식의 논리합, 논리곱
자바와 달리 코틀린은 &
와 |
연산자를 제공하지 않는다. and
와 or
가 각각 &
와 |
를 대신한다.
비교와 동등성
==
: 같다!=
: 같지 않다<
: ~보다 작다<=
: ~보다 작거나 같다>
: ~보다 크다>=
: ~보다 크거나 같다
기본적으로 코틀린 타입은 두 인자가 모두 같은 타입일 때만 ==
와 !=
를 허용한다. 예를 들어, 한 인자가 Int
이고 다른 인자가 Long
이면 ==
를 적용할 수 없다.
값이 박싱돼 있는지에 따라 동등성 연산이 다른 결과를 낳을 수 있는데, 코틀린에서는 박싱이 암시적으로 진행되기 때문에 타입 사이의 동등성 연산을 허용하면 혼란을 야기할 수 있기 때문이다.
하지만 모든 수 타입의 값은 서로 <
, <=
, >
, >=
를 사용해 비교할 수 있다.
문자열
문자열 템플릿
import java.util.Date
fun main() {
val name = readLine()
println("Hello, $name!\nToday is ${Date()}")
}
${}
의 중괄호 사이에 넣기만 하면, 어떤 올바른 코틀린 식이든 문자열에 넣을 수 있다.- 간단한 변수 참조의 경우 중괄호를 생략하고
$
기호만 붙여도 된다.
로우 문자열을 사용하면 이스케이프 시퀀스를 사용하지 않고도 문자열을 작성할 수 있다.
val message = """
Hello, $name!
Today is ${Date()}
""".trimIndent()
trimIndent()
는 여러 줄에 공통된 최소 들여쓰기를 제거해주는 표준 코틀린 함수이다.
기본 문자열 연산
자바에서 문자열 내용을 비교시 equals()
를 사용해야 하지만, 코틀린은 ==
가 기본적으로 equals()
를 가리키는 편의 문법이기 때문에 ==
를 사용하면 직접 equals()
를 호출하므로 따로 equals()
를 호출할 필요가 없다.
만약, 참조 동등성을 쓰고싶으면 ===
와 !==
연산자를 사용하면 된다.
배열
배열 정의하기
배열 구조를 구현하는 가장 일반적인 코틀린 타입은 Array<T>
이다.
val a = emptyArray<String>() // Array<String>
val b = arrayOf("hello", "world") // Array<String>
val c = arrayOf(1, 3, 5) // Array<Int>
Array<Int>
를 사용하는 배열은 제대로 동작하지만 모든 수를 박싱하기 때문에 그다지 실용적이지 못한 해법이다. 이런 이유로 코틀린은 더 효율적인 ByteArray
, ShortArray
, IntArray
, LongArray
, FloatArray
, DoubleArray
, CharArray
, BooleanArray
라는 특화된 배열 타입을 제공한다.
JVM에서 이런 배열 타입들은 int[]
, boolean[]
등의 원시 타입 배열로 표현된다.
val operations = charArrayOf('+', '-')
val squares = IntArray(10) { (it + 1)*(it + 1) }
배열 사용하기
val squares = arrayOf(1, 4, 9, 16)
squares.size // 4
squares.lastIndex // 3
squares[3] // 16
squares[1] // 4
val copySquares = squares.copyOf(2)
copySquares.size // 2
copySquares.lastIndex // 1
copySquares[0] // 1
copySquares[2] // IndexOutOfBoundsException 예외 발생
자바에서는 상위 타입의 배열에 하위 타입의 배열을 대입할 수 있었다. 배열이 가변 데이터 구조이므로 이런 대입은 런타임 시 문제를 발생시킬 수 있다.
Object[] objs = new String[] { "one", "tow", "three" };
objs[0] = new Object(); // ArrayStoreException 예외 발생
이런 이유로 코틀린 배열 타입은 모든 다른 배열 타입과 서로 하위 타입 관계가 성립하지 않는다고 간주되며, 위와 같은 대입도 금지된다.
코틀린에서 String
은 Any
의 하위 타입이지만 Array<String>
은 Array<Any>
의 하위 타입이 아니다.
배열을 생성하고 나면 그 길이를 바꿀 수는 없지만, +
연산을 사용해 원소를 추가한 새로운 배열을 만들 수는 있다.
val a = intArrayOf(1, 2, 3) + 4 // 1, 2, 3, 4
val c = intArrayOf(1, 2) + intArrayOf(3, 4) // 1, 2, 3, 4
문자열과 달리 배열에 대한 ==
와 !=
연산자는 원소 자체를 비교하지 않고 참조를 비교한다.
intArrayOf(1, 2, 3) == intArrayOf(1, 2, 3) // false
배열 내용을 비교하려면 contentEquals()
함수를 사용하면 된다.
intArrayOf(1, 2, 3).contentEquals(intArrayOf(1, 2, 3)) // true
Reference
'🔮 KOTLIN' 카테고리의 다른 글
[코틀린 완벽 가이드] 4장 : 클래스와 객체 다루기 (0) | 2022.07.23 |
---|---|
[코틀린 완벽 가이드] 3장 : 함수 정의하기 (0) | 2022.05.01 |
[코틀린 완벽 가이드] 1장 : 강력하고 실용적인 코틀린 (0) | 2022.04.21 |