ОБНОВЛЕНИЕ - 2014/сент./17
Оказывается, что даже решение в предыдущем обновлении (с 2013 г./фев. 19) не работает, если в качестве первой команды помещается println(Value.Player2)
; то есть ординалы по-прежнему назначаются неправильно.
С тех пор я создал проверяемое рабочее решение как Gist. Реализация ожидает назначения ординалов до тех пор, пока не завершится все инициализация класса/объекта JVM. Это также облегчает расширение/украшение каждого элемента перечисления дополнительными данными, при этом все еще очень эффективно для (де) сериализации.
Я также создал qaru.site/info/1454/..., в котором подробно описаны все различные шаблоны перечислений, используемые в Scala (включая решение в Gist, о котором я упоминал выше).
Я работаю со свежей установкой TypeSafe IDE (Eclipse с предустановленной программой ScalaIDE). Я нахожусь на Windows 7-64bit. И у меня был смешанный успех с Scala Worksheet. Он уже тяжело разбил мою машину (до полного reset или один раз на синий экран смерти) три раза менее чем за час. Таким образом, это может быть ошибкой в рабочем листе Scala. Я еще не уверен, и у меня нет времени, чтобы преследовать эту проблему. Однако этот вопрос перечисления останавливает меня от тестирования.
Я использую следующий код в рабочем листе Scala:
package test
import com.stack_overflow.Enum
object WsTempA {
object Value extends Enum {
sealed abstract class Val extends EnumVal
case object Empty extends Val; Empty()
case object Player1 extends Val; Player1()
case object Player2 extends Val; Player2()
}
println(Value.values)
println(Value.Empty)
}
Вышеописанное работает отлично. Однако, если вы закомментируете первый println, вторая строка выдает исключение: java.lang.ExceptionInInitializerError. И я просто из новичка Scala, чтобы не понять, почему это происходит. Любая помощь будет глубоко оценена.
Здесь трассировка стека с правой стороны рабочего листа Scala (левая сторона снята для отображения здесь):
java.lang.ExceptionInInitializerError
at test.WsTempA$Value$Val.<init>(test.WsTempA.scala:7)
at test.WsTempA$Value$Empty$.<init>(test.WsTempA.scala:8)
at test.WsTempA$Value$Empty$.<clinit>(test.WsTempA.scala)
at test.WsTempA$$anonfun$main$1.apply$mcV$sp(test.WsTempA.scala:14)
at org.scalaide.worksheet.runtime.library.WorksheetSupport$$anonfun$$exe
cute$1.apply$mcV$sp(WorksheetSupport.scala:76)
at org.scalaide.worksheet.runtime.library.WorksheetSupport$.redirected(W
orksheetSupport.scala:65)
at org.scalaide.worksheet.runtime.library.WorksheetSupport$.$execute(Wor
ksheetSupport.scala:75)
at test.WsTempA$.main(test.WsTempA.scala:11)
at test.WsTempA.main(test.WsTempA.scala)
Caused by: java.lang.NullPointerException
at test.WsTempA$Value$.<init>(test.WsTempA.scala:8)
at test.WsTempA$Value$.<clinit>(test.WsTempA.scala)
... 9 more
Класс com.stack_overflow.Enum происходит из qaru.site/info/1454/.... Я добавил в мою версию здесь для простоты (в случае, если я пропустил что-то критическое во время операции копирования/вставки):
package com.stack_overflow
//Copied from /info/1454/case-objects-vs-enumerations-in-scala/19656#19656
abstract class Enum {
type Val <: EnumVal
protected var nextId: Int = 0
private var values_ = List[Val]()
private var valuesById_ = Map[Int ,Val]()
private var valuesByName_ = Map[String,Val]()
def values = values_
def valuesById = valuesById_
def valuesByName = valuesByName_
def apply( id : Int ) = valuesById .get(id ) // Some|None
def apply( name: String ) = valuesByName.get(name) // Some|None
// Base class for enum values; it registers the value with the Enum.
protected abstract class EnumVal extends Ordered[Val] {
val theVal = this.asInstanceOf[Val] // only extend EnumVal to Val
val id = nextId
def bumpId { nextId += 1 }
def compare( that:Val ) = this.id - that.id
def apply() {
if ( valuesById_.get(id) != None )
throw new Exception( "cannot init " + this + " enum value twice" )
bumpId
values_ ++= List(theVal)
valuesById_ += ( id -> theVal )
valuesByName_ += ( toString -> theVal )
}
}
}
Приветствуется любое руководство.
ОБНОВЛЕНИЕ - 2013/февраль/19
После нескольких циклов с Rex Kerr, вот обновленные версии кода, который теперь работает:
package test
import com.stack_overflow.Enum
object WsTempA {
object Value extends Enum {
sealed abstract class Val extends EnumVal
case object Empty extends Val {Empty.init} // <---changed from ...Val; Empty()
case object Player1 extends Val {Player1.init} // <---changed from ...Val; Player1()
case object Player2 extends Val {Player2.init} // <---changed from ...Val; Player2()
private val init: List[Value.Val] = List(Empty, Player1, Player2) // <---added
}
println(Value.values)
println(Value.Empty)
println(Value.values)
println(Value.Player1)
println(Value.values)
println(Value.Player2)
println(Value.values)
package com.stack_overflow
//Copied from /info/1454/case-objects-vs-enumerations-in-scala/19656#19656
abstract class Enum {
type Val <: EnumVal
protected var nextId: Int = 0
private var values_ = List[Val]()
private var valuesById_ = Map[Int ,Val]()
private var valuesByName_ = Map[String,Val]()
def values = values_
def valuesById = valuesById_
def valuesByName = valuesByName_
def apply( id : Int ) = valuesById .get(id ) // Some|None
def apply( name: String ) = valuesByName.get(name) // Some|None
// Base class for enum values; it registers the value with the Enum.
protected abstract class EnumVal extends Ordered[Val] {
val theVal = this.asInstanceOf[Val] // only extend EnumVal to Val
val id = nextId
def bumpId { nextId += 1 }
def compare(that: Val ) = this.id - that.id
def init() { // <--------------------------changed name from apply
if ( valuesById_.get(id) != None )
throw new Exception( "cannot init " + this + " enum value twice" )
bumpId
values_ ++= List(theVal)
valuesById_ += ( id -> theVal )
valuesByName_ += ( toString -> theVal )
}
}
}