스칼라에는 enum
Java와 같은 유형 안전 기능이 없습니다 . 관련 상수 세트가 주어지면 스칼라에서 이러한 상수를 나타내는 가장 좋은 방법은 무엇입니까?
스칼라에는 enum
Java와 같은 유형 안전 기능이 없습니다 . 관련 상수 세트가 주어지면 스칼라에서 이러한 상수를 나타내는 가장 좋은 방법은 무엇입니까?
답변:
http://www.scala-lang.org/docu/files/api/scala/Enumeration.html
사용 예
object Main extends App {
object WeekDay extends Enumeration {
type WeekDay = Value
val Mon, Tue, Wed, Thu, Fri, Sat, Sun = Value
}
import WeekDay._
def isWorkingDay(d: WeekDay) = ! (d == Sat || d == Sun)
WeekDay.values filter isWorkingDay foreach println
}
나는 예라고한다 스칼라 문서 밖으로 복사 에 의해 skaffman은 위의 연습에 제한 유틸리티입니다 (당신 수도뿐만 아니라 사용 case object
의).
Java와 가장 유사한 것을 얻으려면 Enum
(예를 들어 현명 toString
하고 valueOf
메소드가있는-아마도 enum 값을 데이터베이스에 유지하는 경우) 약간 수정해야합니다. skaffman 의 코드를 사용한 경우 :
WeekDay.valueOf("Sun") //returns None
WeekDay.Tue.toString //returns Weekday(2)
다음 선언을 사용하는 반면 :
object WeekDay extends Enumeration {
type WeekDay = Value
val Mon = Value("Mon")
val Tue = Value("Tue")
... etc
}
더 합리적인 결과를 얻습니다.
WeekDay.valueOf("Sun") //returns Some(Sun)
WeekDay.Tue.toString //returns Tue
valueOf
의 replacement is withName
는 옵션을 반환하지 않으며 일치하지 않으면 NSE를 throw합니다. 뭐야!
Map[Weekday.Weekday, Long]
값을 추가 하려고 Mon
하면 컴파일러에서 잘못된 유형 오류가 발생합니다. 예상 요일. 평일에 값을 찾았습니까? 왜 이런 일이 발생합니까?
여러 가지 방법이 있습니다.
1) 기호를 사용하십시오. 그러나 기호가 필요한 기호가 아닌 기호를 허용하지 않는 한 유형 안전성을 제공하지는 않습니다. 나는 여기서 완전성을 위해 언급하고 있습니다. 사용 예는 다음과 같습니다.
def update(what: Symbol, where: Int, newValue: Array[Int]): MatrixInt =
what match {
case 'row => replaceRow(where, newValue)
case 'col | 'column => replaceCol(where, newValue)
case _ => throw new IllegalArgumentException
}
// At REPL:
scala> val a = unitMatrixInt(3)
a: teste7.MatrixInt =
/ 1 0 0 \
| 0 1 0 |
\ 0 0 1 /
scala> a('row, 1) = a.row(0)
res41: teste7.MatrixInt =
/ 1 0 0 \
| 1 0 0 |
\ 0 0 1 /
scala> a('column, 2) = a.row(0)
res42: teste7.MatrixInt =
/ 1 0 1 \
| 0 1 0 |
\ 0 0 0 /
2) 수업 이용 Enumeration
:
object Dimension extends Enumeration {
type Dimension = Value
val Row, Column = Value
}
또는 직렬화하거나 표시해야하는 경우 :
object Dimension extends Enumeration("Row", "Column") {
type Dimension = Value
val Row, Column = Value
}
이것은 다음과 같이 사용될 수 있습니다 :
def update(what: Dimension, where: Int, newValue: Array[Int]): MatrixInt =
what match {
case Row => replaceRow(where, newValue)
case Column => replaceCol(where, newValue)
}
// At REPL:
scala> a(Row, 2) = a.row(1)
<console>:13: error: not found: value Row
a(Row, 2) = a.row(1)
^
scala> a(Dimension.Row, 2) = a.row(1)
res1: teste.MatrixInt =
/ 1 0 0 \
| 0 1 0 |
\ 0 1 0 /
scala> import Dimension._
import Dimension._
scala> a(Row, 2) = a.row(1)
res2: teste.MatrixInt =
/ 1 0 0 \
| 0 1 0 |
\ 0 1 0 /
불행히도, 모든 경기가 설명되는 것은 아닙니다. 일치하는 행이나 열을 잊어 버린 경우 스칼라 컴파일러는 경고하지 않았을 것입니다. 그래서 그것은 나에게 약간의 유형의 안전을하지만만큼 얻을 수 없습니다.
3) 케이스 객체 :
sealed abstract class Dimension
case object Row extends Dimension
case object Column extends Dimension
이제에 사례를 남기지 않으면 match
컴파일러에서 경고합니다.
MatrixInt.scala:70: warning: match is not exhaustive!
missing combination Column
what match {
^
one warning found
그것은 거의 같은 방식으로 사용되며 심지어 필요하지 않습니다 import
:
scala> val a = unitMatrixInt(3)
a: teste3.MatrixInt =
/ 1 0 0 \
| 0 1 0 |
\ 0 0 1 /
scala> a(Row,2) = a.row(0)
res15: teste3.MatrixInt =
/ 1 0 0 \
| 0 1 0 |
\ 1 0 0 /
그렇다면 왜 케이스 객체 대신 Enumeration을 사용하는지 궁금 할 것입니다. 실제로 케이스 객체는 여기와 같이 여러 번 장점이 있습니다. 그러나 Enumeration 클래스에는 반복자,지도, flatMap, 필터 등을 반환하는 요소 (Scala 2.8의 반복자)와 같은 많은 Collection 메소드가 있습니다.
이 답변은 본질적으로 내 블로그 의이 기사 에서 선택한 부분입니다 .
Symbol
인스턴스에 공백이나 특수 문자를 사용할 수 없음 을 의미합니다 . Symbol
수업을 처음 접했을 때 대부분의 사람들은 아마도 그렇게 생각하지만 실제로는 틀립니다. Symbol("foo !% bar -* baz")
컴파일하고 완벽하게 실행합니다. 즉 당신은 완벽하게 만들 수 있습니다 Symbol
포장 인스턴스 모든 문자열을 (그냥 "하나의 혼수 상태"문법 설탕 함께 할 수 없습니다). Symbol
보장 하는 유일한 것은 주어진 심볼의 독창성이므로 비교하고 비교하는 것이 조금 더 빠릅니다.
String
예를 들어 Symbol
매개 변수에 인수 로을 전달할 수 없습니다 .
String
기본적으로 문자열을 감싸는 래퍼 인 다른 클래스로 바꾸면 양방향으로 자유롭게 변환 할 수 있습니다 (의 경우와 같이 Symbol
). "유형 안전을 제공하지 않습니다"라고 말할 때 이것이 의미하는 것 같습니다. OP가 유형 안전 솔루션을 명시 적으로 요구 한 것을 감안할 때 명확하지 않았습니다. 나는 글을 쓰는 시점에 그것들이 전혀 열거 형이 아니기 때문에 유형이 안전 하지 않을 뿐만 아니라 Symbol
전달 된 인수에 특별한 문자가 없다는 것을 보증하지 않는다는 것을 알았는지 확신 하지 못했습니다.
'foo
표기 하지 배제가 비 식별자 문자열). 이것은 미래의 독자를 위해 없애고 싶었던이 오해입니다.
명명 된 열거를 선언하는 약간 덜 장황한 방법 :
object WeekDay extends Enumeration("Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat") {
type WeekDay = Value
val Sun, Mon, Tue, Wed, Thu, Fri, Sat = Value
}
WeekDay.valueOf("Wed") // returns Some(Wed)
WeekDay.Fri.toString // returns Fri
물론 여기서 문제는 이름과 val의 순서를 동기화하여 유지해야한다는 것인데, name과 val이 같은 줄에 선언되어 있으면 더 쉽습니다.
열거 대신 봉인 된 추상 클래스를 사용할 수 있습니다. 예를 들면 다음과 같습니다.
sealed abstract class Constraint(val name: String, val verifier: Int => Boolean)
case object NotTooBig extends Constraint("NotTooBig", (_ < 1000))
case object NonZero extends Constraint("NonZero", (_ != 0))
case class NotEquals(x: Int) extends Constraint("NotEquals " + x, (_ != x))
object Main {
def eval(ctrs: Seq[Constraint])(x: Int): Boolean =
(true /: ctrs){ case (accum, ctr) => accum && ctr.verifier(x) }
def main(args: Array[String]) {
val ctrs = NotTooBig :: NotEquals(5) :: Nil
val evaluate = eval(ctrs) _
println(evaluate(3000))
println(evaluate(3))
println(evaluate(5))
}
}
방금 enumeratum을 발견했습니다 . 그것은 꽤 놀랍고 똑같이 놀랍습니다. 더 잘 알려져 있지 않습니다!
스칼라의 "enumerations"에 관한 모든 옵션에 대한 광범위한 연구를 한 후,이 도메인에 대한 훨씬 더 완전한 개요를 다른 StackOverflow 스레드 에 게시했습니다 . 여기에는 JVM 클래스 / 개체 초기화 순서 문제를 해결 한 "밀봉 된 특성 + 사례 개체"패턴에 대한 솔루션이 포함되어 있습니다.
스칼라에서는 https://github.com/lloydmeta/enumeratum에 매우 편안합니다.
프로젝트는 예제와 문서로 정말 좋습니다.
그들의 문서 에서이 예제만으로 관심을 가져야합니다.
import enumeratum._
sealed trait Greeting extends EnumEntry
object Greeting extends Enum[Greeting] {
/*
`findValues` is a protected method that invokes a macro to find all `Greeting` object declarations inside an `Enum`
You use it to implement the `val values` member
*/
val values = findValues
case object Hello extends Greeting
case object GoodBye extends Greeting
case object Hi extends Greeting
case object Bye extends Greeting
}
// Object Greeting has a `withName(name: String)` method
Greeting.withName("Hello")
// => res0: Greeting = Hello
Greeting.withName("Haro")
// => java.lang.IllegalArgumentException: Haro is not a member of Enum (Hello, GoodBye, Hi, Bye)
// A safer alternative would be to use `withNameOption(name: String)` method which returns an Option[Greeting]
Greeting.withNameOption("Hello")
// => res1: Option[Greeting] = Some(Hello)
Greeting.withNameOption("Haro")
// => res2: Option[Greeting] = None
// It is also possible to use strings case insensitively
Greeting.withNameInsensitive("HeLLo")
// => res3: Greeting = Hello
Greeting.withNameInsensitiveOption("HeLLo")
// => res4: Option[Greeting] = Some(Hello)
// Uppercase-only strings may also be used
Greeting.withNameUppercaseOnly("HELLO")
// => res5: Greeting = Hello
Greeting.withNameUppercaseOnlyOption("HeLLo")
// => res6: Option[Greeting] = None
// Similarly, lowercase-only strings may also be used
Greeting.withNameLowercaseOnly("hello")
// => res7: Greeting = Hello
Greeting.withNameLowercaseOnlyOption("hello")
// => res8: Option[Greeting] = Some(Hello)