Scala Language
열거 형
수색…
비고
스칼라 열거 형에는 몇 가지 문제가 있기 때문에 sealed trait
및 case objects
접근하는 것이 좋습니다.
- 열거 형은 지우기 후에 동일한 유형을 갖습니다.
- 컴파일러는 "Match is not exhaustive"에 대해 불평하지 않습니다. 대문자와 소문자가 일치하지 않으면 런타임에 실패합니다.
scala.MatchError
:
def isWeekendWithBug(day: WeekDays.Value): Boolean = day match {
case WeekDays.Sun | WeekDays.Sat => true
}
isWeekendWithBug(WeekDays.Fri)
scala.MatchError: Fri (of class scala.Enumeration$Val)
비교 대상 :
def isWeekendWithBug(day: WeekDay): Boolean = day match {
case WeekDay.Sun | WeekDay.Sat => true
}
Warning: match may not be exhaustive.
It would fail on the following inputs: Fri, Mon, Thu, Tue, Wed
def isWeekendWithBug(day: WeekDay): Boolean = day match {
^
스칼라 열거에 대한 자세한 설명이이 기사 에서 제공됩니다.
스칼라 열거 형을 사용한 요일
자바와 같은 열거 형은 Enumeration 을 확장하여 만들 수 있습니다.
object WeekDays extends Enumeration {
val Mon, Tue, Wed, Thu, Fri, Sat, Sun = Value
}
def isWeekend(day: WeekDays.Value): Boolean = day match {
case WeekDays.Sat | WeekDays.Sun => true
case _ => false
}
isWeekend(WeekDays.Sun)
res0: Boolean = true
또한 열거 형에서 값에 대해 사람이 읽을 수있는 이름을 추가 할 수도 있습니다.
object WeekDays extends Enumeration {
val Mon = Value("Monday")
val Tue = Value("Tuesday")
val Wed = Value("Wednesday")
val Thu = Value("Thursday")
val Fri = Value("Friday")
val Sat = Value("Saturday")
val Sun = Value("Sunday")
}
println(WeekDays.Mon)
>> Monday
WeekDays.withName("Monday") == WeekDays.Mon
>> res0: Boolean = true
서로 다른 열거 형이 동일한 인스턴스 유형으로 평가 될 수있는 not-so-typesafe 동작을 조심하십시오.
object Parity extends Enumeration {
val Even, Odd = Value
}
WeekDays.Mon.isInstanceOf[Parity.Value]
>> res1: Boolean = true
밀폐 된 특성 및 케이스 객체 사용
Enumeration
을 확장하는 대신 sealed
케이스 객체를 사용합니다.
sealed trait WeekDay
object WeekDay {
case object Mon extends WeekDay
case object Tue extends WeekDay
case object Wed extends WeekDay
case object Thu extends WeekDay
case object Fri extends WeekDay
case object Sun extends WeekDay
case object Sat extends WeekDay
}
sealed
키워드를 사용하면 WeekDay
특성을 다른 파일로 확장 할 수 없습니다. 따라서 컴파일러는 WeekDay
가능한 모든 값이 이미 열거 된 것을 포함하여 특정 가정을 할 수 있습니다.
한 가지 단점은이 방법으로 모든 가능한 값의 목록을 얻을 수 없다는 것입니다. 그러한 목록을 얻으려면 명시 적으로 제공해야합니다.
val allWeekDays = Seq(Mon, Tue, Wed, Thu, Fri, Sun, Sat)
사례 클래스는 또한 sealed
형질을 확장 할 수 있습니다. 따라서 객체와 사례 클래스를 혼합하여 복잡한 계층 구조를 만들 수 있습니다.
sealed trait CelestialBody
object CelestialBody {
case object Earth extends CelestialBody
case object Sun extends CelestialBody
case object Moon extends CelestialBody
case class Asteroid(name: String) extends CelestialBody
}
또 다른 단점은 sealed
객체의 열거 형의 변수 이름에 액세스하거나 그 객체로 검색 할 수있는 방법이 없다는 것입니다. 각 값과 관련된 일종의 이름이 필요하면 수동으로 정의해야합니다.
sealed trait WeekDay { val name: String }
object WeekDay {
case object Mon extends WeekDay { val name = "Monday" }
case object Tue extends WeekDay { val name = "Tuesday" }
(...)
}
아니면 그냥 :
sealed case class WeekDay(name: String)
object WeekDay {
object Mon extends WeekDay("Monday")
object Tue extends WeekDay("Tuesday")
(...)
}
봉인 된 특성 및 케이스 객체와 allValues 매크로 사용
이는 매크로가 컴파일 타임에 모든 인스턴스가있는 세트를 생성하는 봉인 된 특성 변형의 확장 일뿐입니다. 개발자가 열거 형에 값을 추가 할 수 있지만이를 allElements 세트에 추가하는 것을 잊어 버리는 단점은 생략됩니다.
이 변형은 특히 큰 열거 형에 유용합니다.
import EnumerationMacros._
sealed trait WeekDay
object WeekDay {
case object Mon extends WeekDay
case object Tue extends WeekDay
case object Wed extends WeekDay
case object Thu extends WeekDay
case object Fri extends WeekDay
case object Sun extends WeekDay
case object Sat extends WeekDay
val allWeekDays: Set[WeekDay] = sealedInstancesOf[WeekDay]
}
이 작업을하려면 다음 매크로가 필요합니다.
import scala.collection.immutable.TreeSet
import scala.language.experimental.macros
import scala.reflect.macros.blackbox
/**
A macro to produce a TreeSet of all instances of a sealed trait.
Based on Travis Brown's work:
http://stackoverflow.com/questions/13671734/iteration-over-a-sealed-trait-in-scala
CAREFUL: !!! MUST be used at END OF code block containing the instances !!!
*/
object EnumerationMacros {
def sealedInstancesOf[A]: TreeSet[A] = macro sealedInstancesOf_impl[A]
def sealedInstancesOf_impl[A: c.WeakTypeTag](c: blackbox.Context) = {
import c.universe._
val symbol = weakTypeOf[A].typeSymbol.asClass
if (!symbol.isClass || !symbol.isSealed)
c.abort(c.enclosingPosition, "Can only enumerate values of a sealed trait or class.")
else {
val children = symbol.knownDirectSubclasses.toList
if (!children.forall(_.isModuleClass)) c.abort(c.enclosingPosition, "All children must be objects.")
else c.Expr[TreeSet[A]] {
def sourceModuleRef(sym: Symbol) = Ident(sym.asInstanceOf[scala.reflect.internal.Symbols#Symbol
].sourceModule.asInstanceOf[Symbol]
)
Apply(
Select(
reify(TreeSet).tree,
TermName("apply")
),
children.map(sourceModuleRef(_))
)
}
}
}
}