1. Variable & Type System

변수 & 타입 시스템

코틀린은 “기본 불변(immutable), 명시적 null, 명시적 숫자 변환, 객체처럼 보이지만 런타임은 원시 타입 최적화”가 핵심입니다. 실무에서는 val 우선, nullable은 ?로 드러내고, 컬렉션은 외부엔 불변으로 공개하세요.

1) var vs val — 코틀린의 기본 철학

  • val : 재할당 불가(읽기 전용). 기본값으로 사용 권장

  • var : 재할당 가능(가변)

val port = 8080     // 불변
var retries = 3     // 가변
retries += 1

자바와 무엇이 다른가?

  • Java: 기본 가변, 필요하면 final

  • Kotlin: 가변/불변을 선언 시 강제 → 실수 줄고, 병행성에 유리

실무 팁

  • 먼저 **전부 val**로 시작 → “정말 필요할 때만” var.

  • val 컬렉션은 “참조만” 불변입니다. 내용까지 불변으로 노출하려면 불변 인터페이스(List, Set)를 공개하세요.


2) 타입 표기 & 타입 추론

  • 표기 순서: 이름: 타입

  • 초기값이 있으면 대부분 타입 생략 가능

val name: String = "Kotlin"
val count = 10           // Int로 추론
  • 초기화 미루면 타입 필요

var title: String
title = "Hello"

3) 원시 타입은 “객체처럼 보이되, 원시로 컴파일”

  • 코드에선 Int, Long, Boolean클래스처럼 보임

  • 컴파일러가 가능하면 JVM primitive로 최적화 (성능은 Java와 동일)

  • 제네릭/nullable 등 불가피한 경우에만 박싱됨

숫자 변환은 명시적

자바의 암시적 확대 변환(예: int -> long) 없음. 항상 toXxx() 사용.

val i: Int = 1
val l: Long = i.toLong()   // 반드시 변환 호출

4) Null-safety: 타입으로 null을 “보이게” 만들기

  • 기본은 non-null: String

  • null 허용은 ?: String?

val a: String = "ok"
// a = null  // 컴파일 에러

val b: String? = null
println(b?.length ?: 0)   // safe-call + elvis

핵심 도구

  • Safe call ?. : null이면 호출/접근 스킵 → 결과 null

  • Elvis ?: : null일 때 기본값

  • Non-null 단언 !! : “진짜 null 아님”을 보장할 때만 (실수 시 NPE)

자바와 섞일 때(플랫폼 타입)

  • 자바에서 넘어온 값은 null 정보가 불명확할 수 있음 → 직접 null 방어 또는 자바에 @Nullable/@NotNull 붙이기


5) 실무 패턴 모음

A. Android ViewModel: 백킹 프로퍼티로 “읽기 전용” 공개

class CounterViewModel : ViewModel() {
    private val _count = MutableLiveData(0) // 내부에서만 변경
    val count: LiveData<Int> get() = _count // 외부엔 읽기 전용

    fun inc() { _count.value = (_count.value ?: 0) + 1 }
}
  • 공개는 불변 타입(LiveData), 내부만 가변(MutableLiveData)

B. 서버 DTO: null/기본값으로 NPE 차단

data class UserDto(
    val id: Long,
    val name: String,
    val nickname: String?,                 // 없으면 null
    val tags: List<String> = emptyList()   // 기본값으로 빈 리스트
)
  • “옵션 필드”는 ?로, “컬렉션”은 기본값으로 방어

C. DI (Hilt/Dagger/Koin): 생성자 주입 + 불변 프로퍼티

class Repo @Inject constructor(
    private val api: ApiService,    // val로 저장(재할당 불가)
    private val cache: Cache
)
  • 보일러플레이트 감소 + null/재할당 위험 차단

  • 필드 주입이 필요하면 lateinit var (초기화 전 접근 금지 주의)

D. 불변 상태 모델링 (Compose/StateFlow)

data class UiState(val user: User?, val loading: Boolean = false)

class VM : ViewModel() {
    private val _ui = MutableStateFlow(UiState(null, true))
    val ui: StateFlow<UiState> = _ui

    fun loaded(u: User) { _ui.value = UiState(u, false) } // 새 객체로 교체
}
  • 불변 데이터 클래스 + copy/new로 상태 관리 → 버그/레이스 감소


6) 흔한 실수 & 방어법

  • ❌ 숫자 암시적 변환 가정 → ✅ toLong(), toDouble()항상 명시적 변환

  • !! 남발 → ✅ ?., ?:, let, 조기 return으로 재구성

  • var로 모두 선언 → ✅ 기본은 val, 컬렉션은 외부에 불변 인터페이스로 공개

  • ❌ DTO/상태 모델에 가변 프로퍼티 → ✅ data class + val 조합, 변경은 copy()

  • ❌ 자바 혼용 시 null 가정 → ✅ 자바에 어노테이션 부여 또는 코틀린 쪽에서 방어적 처리

Last updated