1. Variable & Type System
변수 & 타입 시스템
코틀린은 “기본 불변(immutable), 명시적 null, 명시적 숫자 변환, 객체처럼 보이지만 런타임은 원시 타입 최적화”가 핵심입니다. 실무에서는 val
우선, nullable은 ?
로 드러내고, 컬렉션은 외부엔 불변으로 공개하세요.
1) var
vs val
— 코틀린의 기본 철학
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이면 호출/접근 스킵 → 결과 nullElvis
?:
: 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