if
식과 return
, 그리고 Nothing
타입if
는 "식(Expression)"이다자바의 3항 연산자 (조건 ? 값1 : 값2
)는 코틀린에 없습니다.
하지만 코틀린의 if
는 단순히 코드 흐름을 제어하는 **"문(Statement)"일 뿐만 아니라, 결과 값을 가질 수 있는 "식(Expression)"**으로도 사용될 수 있습니다. 이것이 3항 연산자의 부재를 상당 부분 보완합니다.
val max = if (a > b) a else b // if 식이 값을 반환하여 max 변수에 할당됨
if
식 내부에서 return
사용의 편리함때로는 if
식의 특정 조건에서 함수를 즉시 종료하고 값을 반환해야 할 때가 있습니다. 이때 if
식 내부에 return
을 사용하면 코드가 간결해질 수 있습니다.
fun renamePackage(fullName: String, newName: String): String {
val i = fullName.lastIndexOf('.') // 마지막 '.'의 위치를 찾음
// if 식이 prefix 변수에 값을 할당하는 데 사용됨
val prefix = if (i >= 0) {
fullName.substring(0, i + 1) // '.'이 있으면, 그 부분까지를 prefix로 사용
} else {
// '.'이 없으면 (패키지 이름이 아니라 단순 이름이면)
// prefix를 계산할 필요 없이 바로 newName을 반환하고 함수 종료
return newName
}
// 위 if-else 문에서 return newName이 실행되지 않은 경우에만 이 코드가 실행됨
return prefix + newName
}
위 예제에서 if (i >= 0)
조건이 false
일 경우 (else
블록 실행), return newName
을 만나 renamePackage
함수는 그 즉시 newName
을 반환하며 종료됩니다. prefix
변수에 실제로 어떤 값이 할당되는 과정은 일어나지 않고, 그 아래의 return prefix + newName
코드도 실행되지 않습니다
Nothing
타입의 등장여기서 중요한 개념이 Nothing
타입입니다.
return
문은 어떤 값을 계산해서 반환한다기보다는, **"이 함수는 여기서 끝난다"**라는 제어 흐름의 변경을 나타냅니다.return
문 자체를 하나의 "식"으로 간주할 때, 이 식이 어떤 타입의 값을 만들어낸다고 봐야 할까요? return
은 값을 만들어내는 게 아니라 함수를 끝내버리는데 말이죠.Nothing
타입입니다. Nothing
은 "값이 존재하지 않음" 또는 "코드가 이 지점에 정상적으로 도달하여 값을 만들어내지 않음"을 명시적으로 나타내는 특별한 타입입니다.
throw Exception()
), 무한 루프에 빠지거나, 아니면 여기서처럼 return
을 통해 함수 실행을 중단시키는 경우, 해당 표현식의 타입은 Nothing
으로 간주될 수 있습니다.renamePackage
예제에서 else return newName
부분의 return newName
이라는 표현식의 타입은 String
이 아니라 Nothing
입니다. newName
이라는 값이 함수의 반환 값으로 사용되지만, return newName
이라는 표현식 자체는 어떤 값을 만들어내지 않고 제어 흐름을 끝내버리기 때문입니다.Nothing
타입의 특징: 모든 타입의 하위 타입Nothing
타입의 매우 중요한 특징은 코틀린의 모든 다른 타입의 하위 타입(subtype)으로 간주된다는 점입니다. (Any
가 모든 타입의 최상위 타입인 것과 반대 개념으로 생각할 수 있습니다.)
이것이 왜 중요할까요? if
식이 값을 만들어내야 하는 상황을 생각해 봅시다.
val prefix = if (i >= 0) fullName.substring(0, i + 1) // String 타입
else return newName // Nothing 타입
if
식의 각 브랜치(if
참일 때, else
일 때)는 같은 타입이거나 호환되는 타입의 값을 반환해야 합니다.
fullName.substring(0, i + 1)
의 결과는 String
타입입니다.return newName
표현식의 타입은 Nothing
입니다.Nothing
이 모든 타입의 하위 타입이기 때문에, String
타입이 와야 할 자리에 Nothing
타입이 와도 문법적으로 문제가 없습니다. 마치 Int
타입 변수에 Int
의 하위 타입인 (만약 존재한다면) SmallInt
값을 넣을 수 있는 것처럼요.
따라서 prefix
변수에 값을 할당하는 if
식에서 한쪽 브랜치는 String
을, 다른 쪽 브랜치는 Nothing
(을 반환하는 return
문)을 가져도 타입 에러가 발생하지 않습니다. 어차피 else
블록의 return
이 실행되면 prefix
변수에 값이 할당될 일이 없기 때문입니다.
Unit
과 Nothing
의 차이Unit
:
void
와 비슷하지만, Unit
은 실제 타입이고 Unit
객체라는 단 하나의 인스턴스를 가집니다.fun printHello() { println("Hello") }
), 사실은 Unit
을 반환하는 것입니다.