Scala перечисления с объектами Singleton в качестве элементов перечисления и возможность их перебора?
Я уже рассмотрел Scala вопрос об эмуляции Java enum
и классов case vs. Enumeration но это кажется слишком большим усилием для слишком низкой выгоды.
В принципе, я бы хотел, чтобы метод values
возвращал все одноэлементные объекты DayOfWeek
, не повторяясь несколько раз.
Вот как выглядит мой код:
object DayOfWeek extends MyEnum {
object MONDAY extends DayOfWeek(1)
object TUESDAY extends DayOfWeek(2)
object WEDNESDAY extends DayOfWeek(3)
object THURSDAY extends DayOfWeek(4)
object FRIDAY extends DayOfWeek(5)
object SATURDAY extends DayOfWeek(6)
object SUNDAY extends DayOfWeek(7)
}
class DayOfWeek(ordinal: Int)
Метод values
должен возвращать что-то вроде, если бы оно было написано так:
val values = Array(MONDAY, TUESDAY, WEDNESDAY, THURSDAY,
FRIDAY, SATURDAY, SUNDAY)
Все должно происходить в признаке MyEnum
, поэтому мне нужно только расширить его, чтобы получить функциональность.
trait MyEnum {
val values = this.getClass.getField("MODULE$") etc. etc.
}
Любое предложение, как это можно сделать точно?
Идея состоит в том, что values
обращается к классу и находит все одноэлементные объекты класса, который они расширяют.
Изменить: похоже, что все предложения не учитывают, что пользователь может создавать объекты, которые, конечно, должны быть сопоставимы с определенными.
Я попытаюсь привести еще один пример, возможно, это более понятно:
object MonthDay extends MyEnum {
//Some important holidays
object NewYear extends MonthDay( 1, 1)
object UnityDay extends MonthDay(11, 9)
object SaintNicholas extends MonthDay(12, 6)
object Christmas extends MonthDay(12, 24)
}
class MonthDay(month: Int, day: Int)
//Of course the user can create other MonthDays
val myBirthDay = new MonthDay(month, day)
if(!MonthDay.values.contains(myBirthDay)) "Well, I probably have to work"
else "Great, it is a holiday!"
Я хочу иметь черту (MyEnum
), которую я могу смешивать с объектом, содержащим мои "объекты перечисления", с методами, чтобы вернуть их список (def values: List[MonthDay]
) или перебрать их (def next: MonthDay
или def previous: MonthDay
).
PPS: Я создал новый вопрос для второй части этого вопроса по просьбе Кена Блума.
Ответы
Ответ 1
Как насчет этого? Это требует, чтобы вы фактически вызывали метод add
для каждого нового значения, но values
возвращает правильный тип.
abstract class MyEnum{
type Value //define me to be the value type for this MyEnum
private var _values:List[Value] = Nil
def values = _values
protected def add(newValue:Value) = {
_values = newValue::_values
newValue
}
}
object DayOfWeek extends MyEnum{
class Value(val dayNum:Int)
val SUNDAY = add(new Value(1))
val MONDAY = add(new Value(2))
val TUESDAY = add(new Value(3))
val WEDNESDAY = add(new Value(4))
val THURSDAY = add(new Value(5))
val FRIDAY = add(new Value(6))
val SATURDAY = add(new Value(7))
}
Теперь вы можете позвонить
println(DayOfWeek.values map (_.dayNum))
Если вам нужны одноэлементные объекты, которые имеют разные определения методов для разных объектов, вы можете создать такие анонимные классы:
add(new Value{
override def dayNum=8
})
Ответ 2
scala.Enumeration делает именно то, что вы хотите уже.
Я думаю, вас может смутить Scala 2.7 по сравнению с Scala 2.8. Старый вопрос, который вы цитируете о эмуляции Java enum
, был написан в дни Scala 2.7, и хотя я не могу проверить, какая функциональность Scala 2.7 Enumeration
, Scala 2.8 Enumeration
, безусловно, обладает всем, что вы ищете.
Вы не можете определить значения с помощью object SUNDAY extends Value(1)
, потому что object
инициализируется лениво.
Ответ 3
Ближе всего я смог придумать следующее:
abstract class MyEnum(val displayName:String){
protected object Value{
var _values:List[Value] = Nil
def values = _values
}
protected class Value (val value:Int){
Value._values = this::Value._values
override def toString = "%s(%d)".format(displayName,value)
}
def values = Value.values
}
trait Methods{
def dayName
}
object DayOfWeek extends MyEnum("DayOfWeek"){
val SUNDAY = new Value(1) with Methods{
override def dayName = "Sunday"
}
val MONDAY = new Value(2) with Methods{
override def dayName = "Monday"
}
val TUESDAY = new Value(3) with Methods{
override def dayName = "Tuesday"
}
val WEDNESDAY = new Value(4) with Methods{
override def dayName = "Wednesday"
}
val THURSDAY = new Value(5) with Methods{
override def dayName = "Thursday"
}
val FRIDAY = new Value(6) with Methods{
override def dayName = "Friday"
}
val SATURDAY = new Value(7) with Methods{
override def dayName = "Saturday"
}
}
Я не понял, как изменить тип переменной _values
, чтобы захватить полный тип Value with Methods
.