UPDATE- 2014年9月17日
事实证明,即使先前更新(从 2013 年 2 月 19 日起)中的解决方案无法工作如果一个地方println(Value.Player2)
作为第一个命令;即序数仍然分配不正确。
从那以后我创建了一个可验证的工作解决方案作为要点 https://gist.github.com/chaotic3quilibrium/57add1cd762eb90f6b24。该实现等待分配序数,直到after所有 JVM 类/对象初始化完成。它还有助于使用附加数据扩展/装饰每个枚举成员,同时仍然非常有效地进行(反)序列化。
我还创建了一个堆栈溢出答案 https://stackoverflow.com/a/25923651/501113它详细说明了 Scala 中使用的所有不同的枚举模式(包括我上面提到的要点中的解决方案)。
我正在全新安装类型安全 IDE http://typesafe.com/stack/downloads/scala-ide(预装了 ScalaIDE 的 Eclipse)。我使用的是 Windows 7-64 位。我使用 Scala Worksheet 取得了不同程度的成功。它已经在不到一个小时的时间内使我的机器崩溃了三次(完全重置或蓝屏死机)。因此,这可能是 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 类来自这个 StackOverflow 线程 https://stackoverflow.com/a/8620085/501113。为了简单起见,我在这里粘贴了我的版本(以防我在复制/粘贴操作期间错过了一些关键内容):
package com.stack_overflow
//Copied from https://stackoverflow.com/a/8620085/501113
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 )
}
}
}
任何形式的指导将不胜感激。
UPDATE- 2013年2月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 https://stackoverflow.com/a/8620085/501113
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 )
}
}
}