코틀린은 불변을 기본값으로 두고, 대부분의 제어 구조가 값을 반환하는 표현식이다.
val과 var
val name = "철수" // 재할당 불가 (자바의 final)
var age = 20 // 재할당 가능
name = "영희" // 컴파일 에러
age = 21 // OK
val은 재할당만 막는다 — 가리키는 객체의 내부는 바뀔 수 있다.
val list = mutableListOf(1, 2)
list.add(3) // OK — 리스트 내용은 바뀐다
list = mutableListOf() // 컴파일 에러 — 재할당은 막힌다
표현식 지향 — if·when·try가 값이다
// 자바의 삼항 연산자가 코틀린에는 없다. if 자체가 값이라 필요 없다
val max = if (a > b) a else b
val label = when {
score >= 90 -> "A"
score >= 80 -> "B"
else -> "F"
}
val n = try { text.toInt() } catch (e: NumberFormatException) { 0 }
값으로 쓸 때는 else가 필수다 — 컴파일러가 모든 경우에 값이 나오는지 확인해야 하기 때문이다.
when — 강력한 분기
when (x) {
1, 2 -> "하나 또는 둘" // 여러 값
in 3..10 -> "3~10" // 범위
is String -> "문자열 길이 ${x.length}" // 타입 검사 + 스마트 캐스트
else -> "기타"
}
// 인자 없는 when — if-else 체인을 대체한다
when {
user == null -> showLogin()
user.isNew -> showOnboarding()
else -> showMain()
}
함수도 표현식이 될 수 있다
fun double(x: Int) = x * 2 // 단일 표현식 함수. 반환 타입도 추론된다
fun greet(name: String, greeting: String = "안녕") = "$greeting, $name" // 기본 인자
greet("철수")
greet(name = "철수", greeting = "반가워") // 명명 인자 — 순서를 바꿔도 된다
기본 인자 덕분에 오버로딩이 거의 필요 없다 — 자바에서 생성자를 여러 개 만들던 일이 사라진다.
문자열 템플릿
val msg = "이름: $name, 내년 나이: ${age + 1}"
val json = """{ "id": $id }""" // 원시 문자열 — 이스케이프가 필요 없다
면접 함정
- ❌ "val은 불변 객체를 만든다" → 참조만 고정한다. 진짜 불변은
List(읽기 전용)나data class의val프로퍼티로 만든다. - ❌ "코틀린에 삼항 연산자가 있다" → 없다.
if가 표현식이라 필요가 없다.
범위와 반복
for (i in 1..5) { } // 1,2,3,4,5
for (i in 1 until 5) { } // 1,2,3,4
for (i in 5 downTo 1) { }
for (i in 1..10 step 2) { }
for ((i, v) in list.withIndex()) { }
if (x in 1..10) { } // 범위 포함 검사
if (name in listOf("a","b")) { }
가시성
- public — 기본값 (자바는 package-private 이 기본 — 다르다)
- internal — 같은 모듈 안에서만 ← 자바에 없는 개념. 라이브러리 내부 API 에 유용
- protected — 하위 클래스에서만 (자바와 달리 같은 패키지에는 안 보인다)
- private — 같은 파일 또는 클래스 안에서만
기본 타입은 클래스다
val a: Int = 1 // 원시 타입이 따로 없다. 컴파일 시 int 로 최적화된다
val b: Int? = 1 // nullable 이면 Integer 로 박싱된다
1.toLong() · "1".toInt() · "1".toIntOrNull() // 암묵적 변환이 없다. 명시적으로
암묵적 숫자 변환이 없다는 게 자바와 다른 점이다 — int를 long 자리에 그냥 못 넣는다. 실수로 인한 정밀도 손실을 막는 설계다.