
코틀린에서 자주 등장하는 ?와 !!는 널 안전성(Null Safety) 과 관련된 아주 중요한 연산자입니다.
이 두 가지는 코틀린 문법에서 객체가 null(널) 이 될 수 있느냐, 없느냐를 명확히 표현하는 역할을 합니다.
코틀린에서 타입 뒤에 ? 를 붙이면 null 값을 허용한다는 의미입니다.
즉, 이 변수가 널(값이 없는 상태) 이 될 수 있음을 명시적으로 표시합니다.
✅ 예시
var name: String? = "1hyung" // String 타입인데 null도 허용
name = null // 가능 (name이 String?이기 때문)
만약 물음표(?) 없이 타입을 선언하면 널을 허용하지 않습니다:
var name: String = "1hyung" // String 타입이고 절대 null 불가능
// name = null // 컴파일 에러 발생! (null을 넣을 수 없음)
널 허용 변수에 접근할 때, 변수의 값이 널이 아닌 경우에만 실행하도록 하는 연산자입니다.
만약 널이라면 에러가 나지 않고, 그냥 널을 반환합니다.
✅ 예시
val length = name?.length // name이 null이 아니라면 length를, null이면 null을 반환
println(length) // name이 "1hyung"면 5, null이면 null 출력
이렇게 쓰면 안전하게 사용할 수 있습니다.
var name: String? = null
println(name?.uppercase()) // null 출력 (안전하게 처리됨)
이 연산자는 값이 널인 경우 대신 사용할 값을 지정하는 연산자입니다.
값이 널이면 우측 값을 대신 사용합니다.
✅ 예시
var name: String? = null
val length = name?.length ?: 0
println(length) // 0 출력 (name이 null이라서 0 사용)
name = "1hyung"
println(name?.length ?: 0) // 5 출력 (name이 null이 아니어서 name의 길이 사용)
즉, 값이 있을 때는 해당 값을 사용하고, 값이 없을 때만 오른쪽 값을 사용합니다.
!! 는 "이 값은 절대 null이 아니다!" 라고 개발자가 강제로 단언 하는 연산자입니다.
만약 이때 실제 값이 null이라면 실행할 때 (Runtime) NullPointerException (NPE) 이 발생하게 됩니다.
✅ 예시
var name: String? = "1hyung"
println(name!!.uppercase()) // "1hyung" 출력, 문제 없음!
name = null
println(name!!.uppercase()) // 실행 중 NullPointerException 에러 발생!
즉, !! 연산자는 코틀린의 안정적인 널 처리를 무시하고 강제로 Java처럼 에러 위험성을 감수하는 연산자입니다.
가급적이면 사용을 지양해야 하지만, 정말로 이 값이 절대 널이 될 수 없다고 확신할 때만 사용하는 게 좋습니다.
다음과 같이 실제 코틀린 코드에서 활용됩니다:
// 사용자 입력이 있을 수도, 없을 수도 있을 때 사용
fun printUserInput(input: String?) {
val safeInput = input ?: "입력이 없습니다." // 입력 없을 때 기본값 지정
println("입력값: $safeInput")
}
// 절대 null이면 안되는 값에서 사용 (사용 주의!)
fun forceNonNull(name: String?) {
println("이름 길이: ${name!!.length}") // name이 null이면 런타임 에러 발생!
}
// 안전한 호출 예시 (널이어도 에러 안남)
fun safeUpperCase(name: String?) {
println(name?.uppercase() ?: "이름이 없습니다.") // null이면 기본 문자열 출력
}
Q. 코틀린에서 ?, !! 연산자는 각각 언제 쓰이고 어떻게 동작하나요?
A. ? (물음표)는 코틀린에서 널을 허용하는 Nullable 타입을 나타낼 때 사용합니다. 즉, 타입 뒤에 ?를 붙이면 그 변수는 null값을 가질 수 있습니다. 그리고 변수가 null일 때를 대비해서 ?. (Safe call) 연산자로 안전하게 호출할 수 있습니다.
반면에 !! (느낌표 두 개)는 '이 값은 절대 null이 아니다' 라고 강제로 단언할 때 사용하는 연산자입니다. 그러나 만약 실제 값이 null이라면 실행 시점에 NullPointerException이 발생합니다.
따라서 !!는 가능하면 지양하고, 확실히 null이 아닌 경우에만 사용하는 게 좋습니다.
| 연산자 | 의미 | 예시 | 주의사항 |
|---|---|---|---|
| ? | 널 허용(Nullable) 타입 선언 | var name: String? | 널 가능성 있음 |
| ?. | 안전 호출(Safe Call) | name?.length | 널일 때 안전 |
| ?: | 널일 때 기본값 지정 (엘비스 연산자) | name ?: "기본값" | 널 처리 기본값 지정 |
| !! | 널이 아님을 강제 선언 | name!!.length | 널일 때 런타임 에러 |