Scala nie ma typu bezpiecznego enum
s jak Java ma. Biorąc pod uwagę zestaw powiązanych stałych, jaki byłby najlepszy sposób reprezentowania tych stałych w Scali?Jak modelować typy enum bezpieczne dla typu?
Odpowiedz
http://www.scala-lang.org/docu/files/api/scala/Enumeration.html
Przykład korzystania
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
}
Poważnie, aplikacja nie powinna być używana. NIE został naprawiony; wprowadzono nową klasę App, która nie ma problemów wspomnianych przez Schildmeijera. Podobnie jak "object foo rozszerza App {...}" I masz natychmiastowy dostęp do argumentów wiersza poleceń za pośrednictwem zmiennej args. – AmigoNico
scala.Enumeration (którego używasz w powyższym przykładzie kodu "obiektu WeekDay") nie oferuje wyczerpującego dopasowania wzorców. Zbadałem wszystkie różne wzorce wyliczania, które są obecnie używane w Scali, oraz podałem i przejrzałem je w odpowiedzi StackOverflow (w tym nowy wzór, który oferuje najlepsze cechy zarówno scala.Enumeration, jak i wzorzec "sealed trait + case": http: //stackoverflow.com/a/25923651/501113 – chaotic3quilibrium
muszę powiedzieć, że przykład kopiowane z dokumentacją Scala przez skaffman powyżej ma ograniczoną przydatność w praktyce (równie dobrze można użyć case object
s).
W celu uzyskania czegoś najbardziej przypominający Java Enum
(tj z sensownych toString
i valueOf
metod - może jesteś utrzymujących wartości enum w bazie danych) trzeba zmodyfikować go trochę. Jeśli użył skaffman „s kod:
WeekDay.valueOf("Sun") //returns None
WeekDay.Tue.toString //returns Weekday(2)
Zważywszy, stosując następującą deklarację:
object WeekDay extends Enumeration {
type WeekDay = Value
val Mon = Value("Mon")
val Tue = Value("Tue")
... etc
}
uzyskać wyniki bardziej sensowne:
WeekDay.valueOf("Sun") //returns Some(Sun)
WeekDay.Tue.toString //returns Tue
Metoda btw. valueOf jest już martwa :-( – greenoldman
@macias Zastąpienie 'valueOf' to' withName', który nie zwraca opcji, i wyrzuca NSE, jeśli istnieje nie może się równać – Bluu
@Bluu Możesz dodać valueOf siebie: def valueOf (name: String) = WeekDay.values.find (_. toString == name) aby mieć opcję – centr
Nieco mniej gadatliwy drodze deklarowanie nazwanych wyliczeń:
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
Oczywiście problem polega na tym, że należy zachować synchronizację nazw i oznaczeń, co jest łatwiejsze, gdy nazwy i wartości są zadeklarowane w tym samym wierszu.
Wygląda to na pierwszy rzut oka czystsze, ale ma tę wadę, że wymaga od opiekuna utrzymania synchronizacji obu list. Dla przykładu dni tygodnia nie wydaje się prawdopodobne. Ogólnie można jednak dodać nową wartość lub jedną usunąć, a dwie listy mogą być niezsynchronizowane, w takim przypadku można wprowadzić subtelne błędy. –
Według wcześniejszego komentarza istnieje ryzyko, że dwie różne listy mogą po cichu przestać zsynchronizować się.Chociaż nie jest to problemem dla twojego obecnego małego przykładu, jeśli jest o wiele więcej członków (jak w dziesiątkach do setek), szanse na dwie listy po cichu znikają, jest znacznie wyższa. Również scala.Enumeration nie może korzystać z wyczerpujących ostrzeżeń/błędów pasujących do wzorca w Scala. Utworzyłem odpowiedź StackOverflow, która zawiera rozwiązanie wykonujące kontrolę środowiska wykonawczego, aby zapewnić synchronizację dwóch list: http://stackoverflow.com/a/25923651/501113 – chaotic3quilibrium
Istnieje wiele sposobów robienia.
1) Użyj symboli. Nie zapewni to jednak żadnego bezpieczeństwa, poza tym, że nie akceptuje nie-symboli, gdy oczekiwany jest symbol. Wspominam o tym tylko tutaj dla kompletności. Oto przykład użycia:
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) Korzystanie z klasy Enumeration
:
object Dimension extends Enumeration {
type Dimension = Value
val Row, Column = Value
}
lub, jeśli trzeba do serializacji lub wyświetlić go:
object Dimension extends Enumeration("Row", "Column") {
type Dimension = Value
val Row, Column = Value
}
ten może być stosowany tak :
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/
Unfortu nately nie zapewnia, że wszystkie mecze są rozliczane. Jeśli zapomniałbym umieścić wiersz lub kolumnę w meczu, kompilator Scali nie ostrzegłby mnie. Tak więc daje mi pewne bezpieczeństwo, ale nie tak dużo, jak można uzyskać.
3) Case obiektów:
sealed abstract class Dimension
case object Row extends Dimension
case object Column extends Dimension
Teraz, jeśli pominąć sprawę na match
, kompilator ostrzega mnie:
MatrixInt.scala:70: warning: match is not exhaustive!
missing combination Column
what match {
^
one warning found
jest używany dość dużo w ten sam sposób, a nawet nie potrzebować 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/
można się zastanawiać, a następnie, dlaczego nigdy nas e Wyliczenie zamiast obiektów sprawy. W rzeczywistości obiekty typu case mają wiele zalet, na przykład tutaj. Jednak klasa Enumeration ma wiele metod kolekcjonowania, takich jak elementy (iterator w Scala 2.8), która zwraca Iterator, mapę, mapę płaską, filtr itd.
Ta odpowiedź jest w istocie wybraną częścią z this article w moim blogu .
Można użyć szczelną abstrakcyjna klasa zamiast wyliczenia, na przykład:
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))
}
}
Cecha zamknięta z obiektami przypadku jest również możliwa. – Ashalynd
Wzorzec "sealed trait + case objects" zawiera problemy, które szczegółowo opiszę w odpowiedzi StackOverflow. Jednak wymyśliłem, jak rozwiązać wszystkie problemy związane z tym wzorem, który jest również objęty wątkiem: http://stackoverflow.com/a/25923651/501113 – chaotic3quilibrium
Po przeprowadzeniu szeroko zakrojonych badań nad wszystkimi opcjami wokół „wyliczenia” w Scala, Zamieściłem znacznie pełniejszy obraz tego domena na innym StackOverflow thread. Zawiera rozwiązanie wzoru "zapieczętowana cecha + obiekt sprawy", w którym rozwiązałem problem porządkowania klasy JVM/obiektu.
właśnie odkryto enumeratum. To niesamowite i równie niesamowite, że nie jest bardziej znane!
- 1. członkowie enum typu Int32
- 2. Typy danych klas typu
- 3. Java: Generic akceptujące tylko typy enum
- 4. Jak określić typy OrderedDict K, V dla adnotacji typu Mypy?
- 5. Konwersja Enum w podstawowej typu
- 6. Bind char do typu enum
- 7. Wpisz alias dla enum
- 8. Typy generyczne Java typu erasure
- 9. Typy abstrakcyjne a parametry typu
- 10. Jak przypisać wartość logiczną modelować
- 11. Jak modelować w Java EE?
- 12. Wiele podpisy typu dla członków, typy Unii w maszynopisie
- 13. Jak modelować TimeField w Django?
- 14. Bezpieczne korzystanie z przestrzeni typu Haskell
- 15. Parametry typu a typy członków w Scali
- 16. Typy Numpy dla użytkowników Cythona
- 17. Jak wypromować dwa typy szablonów dla operacji arytmetycznych, takich jak typy wbudowane?
- 18. Jak modelować system głosowania "lubi" z MongoDB
- 19. Jak modelować relacje encji w GAEJ?
- 20. Typy RequestCode dla startActivityforResult
- 21. Jak zdefiniować niestandardowe typy dumpów typu float (C-API)?
- 22. Jak powinny być stosowane typy w klasach typu Haskell?
- 23. Tworzenie typu zmiennej ENUM w MySQL
- 24. Enum przypadek '...' nie jest członkiem typu '...'
- 25. Nowe, nowoczesne sprawdzanie typu enum Objective-C
- 26. CQRS - jak modelować system wykonywania scenariuszy
- 27. f # Statycznie rozdzielone typy w elementach typu
- 28. Szablon specjalizacyjny dla enum
- 29. Jak bezpieczne jest moje bezpieczne ponowne rzucenie?
- 30. Mongoose: Jak modelować klucz obcy/odwrotną relację?
Dlaczego nie używać emulacji języka Java? Jest to jedna z niewielu rzeczy, które wolę używać zwykłego java. – Max
Napisałem mały przegląd o wyliczaniu scala i alternatywach, może się okazać, że jest to przydatne: pedrorijo.com/blog/scala-enums/ – pedrorijo91