Language/Kotlin

코틀린 주요 특징 및 기초 문법 다지기

jiihyunn 2025. 4. 17. 03:28

코틀린(Kotlin)이란?

코틀린은 젯브레인(JetBrains)에서 개발한 현대적이고 간결한 프로그래밍 언어입이다.

JVM(Java Virtual Machine)위에서 실행되기 때문에 자바와 100% 호환되는 특징을 지니고 있습니다. 따라서 자바가 사용되고 있는 모든 환경에서 안전하고 효율적인 대체 언어로 활용될 수 있습니다.

 

코틀린 주요 특성

정적 타입 지정 언어 + 타입 추론 가능

타입을 컴파일 시점에 알 수 있으며, 컴파일러가 컴파일 시점에 타입을 검증해줍니다. (자바와 동일) 

자바랑 다른 점은 모든 변수의 타입을 직접 명시하지 않아도 됩니다.

컴파일러가 문맥을 고려해 변수 타입을 결정하는 타입 추론 기능을 제공합니다. 

따라서 코틀린 컴파일러가 문맥으로부터 변수 타입을 자동으로 유추할 수 있기 때문에 타입 선언 생략이 가능합니다.

Null 안정성 지원

Null Pointer Exception(NPE)과 같은 런타임 오류를 방지할 수 있도록 Null 관련 오류를 컴파일 시점에 탐지할 수 있습니다.

'?'를 사용해 Nullable 타입을 선언할 수 있으며, 타입에 ?가 없다면 null값을 허용하지 않습니다.

이 외에도 여러 연산자를 사용하여 null 처리를 간단하게 구현할 수 있습니다.

'?:'연산자를 통해 Null일 경우 기본값을 설정할 수 있으며,

'?.'를 통해 Nullable 타입의 값이 null인지 확인 후, null이 아니면 작업을 수행할 수 있습니다.

함수형 프로그래밍 지원

선언형 프로그래밍 스타일로, 코드를 좀 더 간결하고 직관적으로 작성할 수 있습니다.

함수를 매개변수로 전달하거나 반환값으로 사용할 수 있으며, 람다 표현식을 통해 익명 함수를 간결하게 작성할 수 있습니다.

이 때 불변 데이터 구조를 사용해 상태 변경을 최소화하며, 동일한 입력값에 대해 항상 동일한 결과를 보장합니다.

fun calculate(a: Int, b: Int, operation: (Int, Int) -> Int): Int {
    return operation(a, b)
}

val sum = calculate(5, 3) { x, y -> x + y }
println(sum) // 출력: 8

 

코틀린 문법

변수

var: 선언 후 재할당 가능

val: 선언 후 재할당 불가능 (== 자바의 final)

var a = 123
print(a) // 123
a = 456
print(a) // 456

val b = 789
// b = 1234 (x) 재할당 불가

 

Companion Object (동반 객체)

동반 객체로써, 이름 그대로 클래스 내부에 존재하며 클래스가 메모리에 적재될 때 함께 생성됩니다.

kotlin에서는 자바처럼 static이 존재하지 않기 때문에 Companion Object라는 동반 객체를 사용하여 정적 멤버를 정의할 수 있습니다.

companion object {
        private const val MIN_LENGTH: Int = 1
        private const val MAX_LENGTH: Int = 5
    }

* 자바에서는 아래와 같이 표현할 수 있습니다. 

private static final Integer MIN_LENGTH = 1
private static final Integer MAX_LENGTH = 5

 

init

자바와 달리 생성자 함수가 따로 없기 때문에 init을 통해 객체 초기화 및 검증을 수행할 수 있습니다.

class User(val name: String, val age: Int) {

    init {
        require(age > 0) { "나이는 음수일 수 없습니다." } // 검증 로직
    }
}

val user = User("jihyun", 25) // 정상
val invalidUser = User("amy", -5) // IllegalArgumentException 발생

 

Secondary Constructor (보조 생성자)

 

필요 시 보조 생성자를 정의할 수 있습니다.

class Car(
    private val name: String,
    private val position: Int,
) {
    constructor(name: String) : this(name, 0)
}

val carWithPosition = Car("car1", 10)
val defaultPositionCar = Car("car2")

 

require() {}

 

함수의 매개변수나 입력값을 검증하며, require 함수 내 조건 false인 경우, IllegalArgumentException을 발생시킵니다.

 

check() {}

함수의 상태를 검증하며, require 함수 내 조건 false인 경우, IllegalStateException을 발생시킵니다.

fun validate(amount: Int) {
        check(amount < 0) { "금액이 충분하지 않습니다." } 
    }

 

 

List

listOf(): 불변 -> 요소 생성, 수정, 삭제 불가

mutableListOf(): 가변 -> 추가, 수정, 삭제 가능

fun main() {
    val immutableList = listOf<Int>(1, 2, 3)
    val mutableList = mutableListOf<Int>()

    b.add(1)
    b.add(2)
    
    println(b) // [1,2]
}

 

data class

클래스에 보일러플레이트 코드(hashCode(), equals(), toString(), copy(), componentN())을 자동으로 구현해줍니다.

data class User(val name: String, val age: Int)

fun main() {
    val user1 = User("jihyun", 25)
    println(user1) // 출력: User(name=jihyun, age=25)

    val user2 = user1.copy(name = "jii")
    println(user2) // 출력: User(name=jii, age=25)

    val (name, age) = user1 // 구조 분해
    println("Name: $name, Age: $age") // 출력: Name: jihyun, Age: 25
}

 

lateinit

변수 초기화를 늦출 수 있는 키워드로, 클래스 내부에서 var로 선언된 프로퍼티에만 사용할 수 있습니다.

Non-Nullable 타입에만 사용할 수 있으며, 초기화를 나중에 수행할 수 있습니다.

class RaceHistoryTest {

    private lateinit var raceHistory: RaceHistory

    @BeforeEach
    fun setUp() {
        raceHistory = RaceHistory()
    }
}

 

클래스 프로퍼티

val을 사용해 프로퍼티를 선언하면 getter를 무조건 포함하며,

var을 사용해 프로퍼티를 선언하면 gettter와 setter를 포함합니다.

class Person(val name: String, var age: Int)

fun main() {
    val person = Person("jihyun", 25)
    println(person.name) // 메서드 생성없이 getter 호출 가능
    
    person.age = 20 // 메서드 생성없이 setter 호출 가능
    println(person.age) // 출력: 20
    
}