更新- 2014 年 9 月 17 日
事实证明,如果一个地方作为第一个命令,即使之前更新(从 2013 年 2 月 19 日起)中的解决方案也无法工作;println(Value.Player2)
即序数仍然分配不正确。
从那以后,我创建了一个可验证的工作解决方案作为 Gist。实现等待分配序数,直到所有JVM 类/对象初始化完成。它还有助于使用附加数据扩展/装饰每个枚举成员,同时对于(反)序列化仍然非常有效。
我还创建了一个StackOverflow 答案,详细说明了 Scala 中使用的所有不同枚举模式(包括我上面提到的 Gist 中的解决方案)。
我正在使用全新安装的TypeSafe 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 线程。为了简单起见,我在此处粘贴了我的版本(以防我在复制/粘贴操作期间遗漏了一些关键内容):
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 )
}
}
}
任何形式的指导将不胜感激。
更新- 2013/Feb/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 )
}
}
}