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을 반환하는 것입니다.