Scala에는 ADT 라는 개념이 있습니다. 저는 이걸 배우며 자바에서 흔히 쓰이는 enum이 연상되더라구요. ADT는 Algebraic Data Types
의 약자로, 직역하자면 대수 데이터 타입
입니다.
sealed traits
와 함께 패턴 매칭에서 완전성을 검사할 수 있습니다. 패턴매칭에서 모든 패턴을 매칭하지 않으면 컴파일 에러가 나기 때문입니다.ADT는 scala 에서 데이터 타입을 정의할 때 sealed trait
을 정의하여 사용할 수 있습니다. companion obejct
안에 case class
와 case object
를 사용할 수 있습니다.
그렇다면 이 둘은 각기 용도와 특성이 어떻게 다르며, 언제 쓰일까요? 각 측면에서 비교해보겠습니다.
다양한 생성자 인수로 여러 번 인스턴스화할 수 있습니다. 일관된 구조를 가지면서 변할 수 있는 데이터를 나타낼 때 유용합니다.
sealed trait Animal
object Animal {
case class Dog(name: String) extends Animal
case class Cat(name: String) extends Animal
}
val myDog = Animal.Dog("Buddy")
val myCat = Animal.Cat("Whiskers")
단일, 싱글톤 인스턴스를 나타냅니다. 고정된 데이터 세트나 여러 인스턴스가 필요하지 않은 "객체"를 나타낼 때 유용합니다.
sealed trait 신호등
object 신호등 {
case object 빨강 extends 신호등
case object 초록 extends 신호등
case object 노랑 extends 신호등
}
데이터의 값이 다양할 때 사용됩니다.
다양한 데이터를 포함할 필요가 없지만 다른 타입이나 상태를 나타내고 싶을 때 사용됩니다
생성자 매개변수를 가질 수 있으며 각 인스턴스에 대해 다른 값을 캡슐화할 수 있습니다.
생성자 매개변수를 가질 수 없습니다. 단일, 불변 인스턴스입니다.
case class
와 case object
는 모두 패턴 매칭에 사용할 수 있습니다. 참고로 sealed
키워드는 모든 하위 유형이 같은 파일 내에서 정의되도록 하여 컴파일러가 완전한 패턴 매칭을 확인하도록 합니다.
def describeAnimal(a: Animal): String = a match {
case Animal.Dog(name) => s"A dog named $name"
case Animal.Cat(name) => s"A cat named $name"
}
def checkLight(l: Light): String = l match {
case Light.Red => "Stop"
case Light.Green => "Go"
case Light.Yellow => "Caution"
}
모든 인스턴스화가 메모리에 새 객체를 생성합니다.
단 하나의 인스턴스만 생성되므로 특정 개념이나 상태를 나타낼 때 메모리 효율적입니다.