throw
has a type: Nothing
in Scalathrow
는 왜 타입이 필요할까? 아래 예문을 살펴보자.def error(message: String): Nothing = throw new RuntimeException(message)
def divide(x: Int, y: Int): Int = {
if (y != 0) x / y
else error("can't divide by zero")
}
몇가지 특이한 점을 발견할 수 있다.
Int
이다. 그런데 divid 함수의 본문은 if/else
뿐이며 if
와 else
의 반환 타입이 다르다.Nothing
이다. 그런데 error함수의 본문은 throw
뿐이다. throw
가 되면 실행이 중지될 텐데 타입이 왜 필요할까?이제 궁금한 점을 하나씩 풀어보자.
Scala는 다른 Functional language들과 마찬가지로 모든 문장(statements)은 식(expressions)이다.
즉 모든 문장(식)이 평가되면 반드시 어떠한 결과 값을 반환해야 한다.
때문에 throw
역시 반환값을 가져야 한다.
throw
는 기본적으로 control flow에 관여한다. throw
문이 평가되면 exception
이 발생되기 때문에
반환 타입은 아무런 의미가 없는 것으로 보인다.
그렇다면 throw
는 어떤 타입을 가져야 하며 어떤 의미가 있을까?
throw
의 반환 타입을 repl로 확인해 보자
scala> :type throw new Exception
=> Nothing
반환 타입은Nothing
이다. 어떤 의미가 있을까?
위 그림에서 알 수 있듯이 Nothing
은 모든 타입에 하위타입이다.
즉 어떤 타입이 필요하든 그 자리에는 Nothing
이 위치할 수 있다.
위에서 Scala에서 모든 문장은 식이며 언제나 반환값을 가져야 한다고 했다.
그렇다면 if
는 어떨까? if
역시 마찬가지로 식이며 반환값을 가져야 한다.
확인을 위해 아래 구문을 살펴보자
scala> :type if (true) 10 else "oops"
=> Any
위 식의 반환 타입은 scala.Int
(10)과 java.lang.String
("oops")의 공통 최 상위 타입 Any
다.
식의 평가 값을 할당할 때 type
이 결정 되여야 하기 때문에 공통 조상 타입이 할당된다.
이제 다시 아래 예문을 살펴보자
def error(message: String): Nothing = throw new RuntimeException(message)
def divide(x: Int, y: Int): Int = {
if (y != 0) x / y
else error("can't divide by zero")
}
스칼라는 강타입(static-type)
언어다. 때문에 컴파일 타임에 모든 타입이 결정되야 한다.
위 예문이 컴파일 되려면 if/else
절의 타입이 정해져야 한다. 그렇기 때문에 throw
문의 반환값이 필요하며 적절한 타입이 위치해야 한다.
위 예문에서 divide
함수의 반환값이 Int
이며 if
절이 Int
를 반환하기 때문에 else
절 역시 Int
혹은 그에 해당하는 하위 타입이 위치해야 한다.
이런 이유로 모든 타입에 하위 타입인 Nothing
이 필요하게 된다.
요약하자면
if/else
역시 식이며 반환 타입이 필요하다.throw
에 반환 타입은 실행상 아무 의미가 없지만 컴파일 시 타입 결정에 관여한다.