스칼라는 제네릭 클래스의 타입 파라미터에 대한 가변성 어노테이션
을 지원한다. 스칼라에서는 추상화된 클래스를 정의할 때 가변성 어노테이션을 추가할 수 있다.
다음과 같은 예시 코드를 보자.
class Stack[+T] {
def push[S >: T](elem: S): Stack[S] = new Stack[S] {
override def top: S = elem
override def pop: Stack[S] = Stack.this
override def toString: String =
elem.toString + " " + Stack.this.toString
}
def top: T = sys.error("no element on stack")
def pop: Stack[T] = sys.error("no element on stack")
override def toString: String = ""
}
object VariancesTest extends App {
var s: Stack[Any] = new Stack().push("hello")
s = s.push(new Object())
s = s.push(7)
println(s)
}
위의 코드는 제네릭을 사용한 코드와 매우 유사하다. 다만 달라진 점이 있다면, 클래스 뒤의 제네릭 표시 앞에 +
가 붙은 것이다. 이 키워드의 의미는 무엇일까?
기존의 제네릭 클래스에서는 Stack[T]
와 같은 형태로 제네릭을 선언했다면, T
내부에 한 번 설정한 값은 불변자여야 했다. 즉, T
타입의 값만 들어와야 했다. 이는 컴파일 시 오류를 검출해준다는 장점이 있지만, 추상화된 클래스의 재사용을 제한할 수 있기도 했다. 하지만, 가변성 어노테이션인 +T
를 사용한다면 이를
어노테이션 +T
는 타입 순가변 위치에서만 사용할 수 있는 타입 T
를 선언한다. 반면, -T
는 역가변 위치에서만 사용할 수 있는 어노테이션이다.
String형인 "hello"와 객체인 new Object(), Int형인 7 모두 타입은 다르지만 스택 안에 잘 들어간 것이 확인된다. 이렇게 항목의 타입이 일반화된 스택을 사용하기 위해서는 가변성 어노테이션을 사용하는 것이 좋다.