Scala non ha messaggi di sicurezza enum
come Java. Dato un insieme di costanti correlate, quale sarebbe il modo migliore in Scala di rappresentare quelle costanti?
Scala non ha messaggi di sicurezza enum
come Java. Dato un insieme di costanti correlate, quale sarebbe il modo migliore in Scala di rappresentare quelle costanti?
Risposte:
http://www.scala-lang.org/docu/files/api/scala/Enumeration.html
Esempio di utilizzo
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
}
Devo dire che l'esempio copiato dalla documentazione Scala di skaffman sopra è di utilità limitata in pratica (potresti anche usare case object
s).
Per ottenere qualcosa che assomigli di più a un Java Enum
(cioè con metodi toString
e valueOf
metodi sensibili , forse stai persistendo i valori enum in un database) devi modificarlo un po '. Se avessi usato il codice di skaffman :
WeekDay.valueOf("Sun") //returns None
WeekDay.Tue.toString //returns Weekday(2)
Considerando che usando la seguente dichiarazione:
object WeekDay extends Enumeration {
type WeekDay = Value
val Mon = Value("Mon")
val Tue = Value("Tue")
... etc
}
Ottieni risultati più sensati:
WeekDay.valueOf("Sun") //returns Some(Sun)
WeekDay.Tue.toString //returns Tue
valueOf
La sostituzione di @macias è withName
che non restituisce un'opzione e genera un NSE se non c'è corrispondenza. Che cosa!
Map[Weekday.Weekday, Long]
e aggiungere un valore ditegli Mon
che il compilatore genera un errore di tipo non valido. Weekday atteso. Week-end trovato valore? Perché succede?
Esistono molti modi per farlo.
1) Usa i simboli. Tuttavia, non ti darà alcun tipo di sicurezza, oltre a non accettare non-simboli in cui è previsto un simbolo. Sto solo citandolo qui per completezza. Ecco un esempio di utilizzo:
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) Utilizzando la classe Enumeration
:
object Dimension extends Enumeration {
type Dimension = Value
val Row, Column = Value
}
oppure, se è necessario serializzare o visualizzarlo:
object Dimension extends Enumeration("Row", "Column") {
type Dimension = Value
val Row, Column = Value
}
Questo può essere usato in questo modo:
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 /
Sfortunatamente, non garantisce che tutte le partite vengano prese in considerazione. Se avessi dimenticato di mettere Row o Column nella partita, il compilatore Scala non mi avrebbe avvertito. Quindi mi dà un po 'di sicurezza, ma non quanto si può guadagnare.
3) Oggetti del caso:
sealed abstract class Dimension
case object Row extends Dimension
case object Column extends Dimension
Ora, se lascio un caso su un match
, il compilatore mi avvertirà:
MatrixInt.scala:70: warning: match is not exhaustive!
missing combination Column
what match {
^
one warning found
È usato praticamente allo stesso modo e non ha nemmeno bisogno di un 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 /
Potresti chiederti, quindi, perché mai usare un'enumerazione invece degli oggetti case. È un dato di fatto, gli oggetti caso presentano vantaggi molte volte, come qui. La classe Enumeration, tuttavia, ha molti metodi Collection, come gli elementi (iteratore su Scala 2.8), che restituisce un Iteratore, mappa, flatMap, filtro, ecc.
Questa risposta è essenzialmente una parte selezionata di questo articolo nel mio blog.
Symbol
istanze non possono avere spazi o caratteri speciali. La maggior parte delle persone al primo incontro con la Symbol
classe probabilmente lo pensa, ma in realtà non è corretta. Symbol("foo !% bar -* baz")
compila ed esegue perfettamente bene. In altre parole, puoi creare perfettamente Symbol
istanze avvolgendo qualsiasi stringa (semplicemente non puoi farlo con lo zucchero sintattico "coma singolo"). L'unica cosa che Symbol
garantisce è l'unicità di ogni dato simbolo, che rende leggermente più veloce il confronto e il confronto.
String
argomento, ad esempio, a un Symbol
parametro.
String
con un'altra classe che è sostanzialmente un involucro attorno a una stringa e può essere liberamente convertito in entrambe le direzioni (come nel caso di Symbol
). Immagino sia quello che volevi dire dicendo "Non ti darà alcun tipo di sicurezza", semplicemente non era molto chiaro dato che OP ha chiesto esplicitamente soluzioni di tipo sicuro. Non ero sicuro se al momento della scrittura si sapeva che non solo non è digitare sicuro perché quelli non sono enumerazioni a tutti, ma anche Symbol
s non hanno nemmeno la garanzia che l'argomento passato non avrà i caratteri speciali.
'foo
notazione che fa ostino stringhe non identificative). Questo è questo malinteso che volevo dissipare per qualsiasi lettore futuro.
Un modo leggermente meno prolisso per dichiarare le enumerazioni denominate:
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
Ovviamente il problema qui è che dovrai sincronizzare l'ordinamento dei nomi e dei valori che è più facile da fare se nome e valore sono dichiarati sulla stessa riga.
È possibile utilizzare una classe astratta sigillata anziché l'enumerazione, ad esempio:
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))
}
}
appena scoperto l' enumeratum . è abbastanza sorprendente e altrettanto sorprendente non è più noto!
Dopo aver fatto ricerche approfondite su tutte le opzioni relative alle "enumerazioni" in Scala, ho pubblicato una panoramica molto più completa di questo dominio su un altro thread StackOverflow . Include una soluzione al modello "trait + seal case case" in cui ho risolto il problema di ordinazione di inizializzazione di oggetti / classe JVM.
In Scala è molto comodo con https://github.com/lloydmeta/enumeratum
Il progetto è davvero buono con esempi e documentazione
Solo questo esempio dai loro documenti dovrebbe farti interessare
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)