Scala,为什么 lazy val 在枚举中表现得如此神秘?
Why does lazy val behave so mysteriously in enumeration, Scala?
我想懒惰地找到所有定义的枚举值名称中的最大值。下面这段代码执行时陷入死循环,重复打印InvocationTargetException
.
object Enum extends Enumeration with App {
val A, B = new Val
lazy val foo = values.maxBy(_.toString)
println(Enum.foo)
}
当foo
为def
时不会出现该问题。
为什么会这样? lazy val
不应该只是一个背诵的def
吗?
奇怪的是,以下代码确实按预期工作:
object Enum extends Enumeration with App {
val A, B = new Val
lazy val foo = values.foldLeft(""){(a, b) => a + b}
println(Enum.foo)
}
我正在使用 scala 2.11.7。
// --- 解决方案
问题是 foo 是 Value
类型,因此被解释为枚举值。
解决该问题的方法是创建一个 def
到 lazy val
的代理,如以下代码片段所示:
object Enum extends Enumeration with App {
val A, B = new Val
private lazy val fooBuffer = values.maxBy(_.toString)
def foo = fooBuffer
println(Enum.foo)
}
两个问题是App
必须由Enum main null
初始化。
然后,Enumeration
查找 Val
类型的 vals,其中包括您的成员 foo
,这可能不是预期的。
示例混乱:
scala> :pa
// Entering paste mode (ctrl-D to finish)
object Enum extends Enumeration with App {
val A, B = new Val
lazy val foo = values.maxBy(_.toString)
println(Enum.foo)
}
// Exiting paste mode, now interpreting.
defined object Enum
scala> Enum main null
foo
scala> Enum.foo
res2: Enum.Value = foo
scala> Enum.values
res3: Enum.ValueSet = Enum.ValueSet(A, foo)
只是为了表明修复类型修复了 SO:
scala> object X extends Enumeration { val x, y, z = new Val ; lazy val f: Any = values.maxBy(_.toString) }
defined object X
scala> X.f
res2: Any = z
我不知道你是如何初始化你的对象的,但我看到的是 WhosebugError
(无论你是否将初始化延迟到 App
):
java.lang.WhosebugError
at java.lang.reflect.Method.getParameterTypes(Method.java:264)
at scala.Enumeration$$anonfun.apply(Enumeration.scala:161)
at scala.Enumeration$$anonfun.apply(Enumeration.scala:161)
at scala.collection.TraversableLike$$anonfun$filterImpl.apply(TraversableLike.scala:259)
at scala.collection.IndexedSeqOptimized$class.foreach(IndexedSeqOptimized.scala:33)
at scala.collection.mutable.ArrayOps$ofRef.foreach(ArrayOps.scala:186)
at scala.collection.TraversableLike$class.filterImpl(TraversableLike.scala:258)
at scala.collection.TraversableLike$class.filter(TraversableLike.scala:270)
at scala.collection.mutable.ArrayOps$ofRef.filter(ArrayOps.scala:186)
at scala.Enumeration.scala$Enumeration$$populateNameMap(Enumeration.scala:161)
at scala.Enumeration$$anonfun$scala$Enumeration$$nameOf.apply(Enumeration.scala:180)
at scala.Enumeration$$anonfun$scala$Enumeration$$nameOf.apply(Enumeration.scala:180)
at scala.collection.MapLike$class.getOrElse(MapLike.scala:128)
at scala.collection.AbstractMap.getOrElse(Map.scala:59)
at scala.Enumeration.scala$Enumeration$$nameOf(Enumeration.scala:180)
at scala.Enumeration$Val.toString(Enumeration.scala:223)
at Enum$$anonfun$foo.apply(<console>:55)
at Enum$$anonfun$foo.apply(<console>:55)
at scala.collection.TraversableOnce$$anonfun$maxBy.apply(TraversableOnce.scala:241)
at scala.collection.TraversableOnce$$anonfun$maxBy.apply(TraversableOnce.scala:240)
at scala.collection.Iterator$class.foreach(Iterator.scala:742)
at scala.collection.AbstractIterator.foreach(Iterator.scala:1194)
at scala.collection.IterableLike$class.foreach(IterableLike.scala:72)
at scala.collection.AbstractIterable.foreach(Iterable.scala:54)
at scala.collection.TraversableOnce$class.maxBy(TraversableOnce.scala:240)
at scala.collection.AbstractTraversable.maxBy(Traversable.scala:104)
at Enum$.foo$lzycompute(<console>:55)
at Enum$.foo(<console>:55)
at sun.reflect.GeneratedMethodAccessor66.invoke(Unknown Source)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
at java.lang.reflect.Method.invoke(Method.java:497)
at scala.Enumeration$$anonfun$scala$Enumeration$$populateNameMap.apply(Enumeration.scala:168)
at scala.Enumeration$$anonfun$scala$Enumeration$$populateNameMap.apply(Enumeration.scala:165)
...
原因很可怕 source code 是在做一些肮脏的反射启发式算法。基本上 populateNameMap
在枚举的主体中查找类型 Value
的值。由于 lazy val foo
的类型 Value
包含在 populateNameMap
的主体中。所以你在这里有一个无限循环。
当您从 lazy val
更改为 def
时,populateNameMap
不再包含 foo
,因为它不是 ValDef
。
我想懒惰地找到所有定义的枚举值名称中的最大值。下面这段代码执行时陷入死循环,重复打印InvocationTargetException
.
object Enum extends Enumeration with App {
val A, B = new Val
lazy val foo = values.maxBy(_.toString)
println(Enum.foo)
}
当foo
为def
时不会出现该问题。
为什么会这样? lazy val
不应该只是一个背诵的def
吗?
奇怪的是,以下代码确实按预期工作:
object Enum extends Enumeration with App {
val A, B = new Val
lazy val foo = values.foldLeft(""){(a, b) => a + b}
println(Enum.foo)
}
我正在使用 scala 2.11.7。
// --- 解决方案
问题是 foo 是 Value
类型,因此被解释为枚举值。
解决该问题的方法是创建一个 def
到 lazy val
的代理,如以下代码片段所示:
object Enum extends Enumeration with App {
val A, B = new Val
private lazy val fooBuffer = values.maxBy(_.toString)
def foo = fooBuffer
println(Enum.foo)
}
两个问题是App
必须由Enum main null
初始化。
然后,Enumeration
查找 Val
类型的 vals,其中包括您的成员 foo
,这可能不是预期的。
示例混乱:
scala> :pa
// Entering paste mode (ctrl-D to finish)
object Enum extends Enumeration with App {
val A, B = new Val
lazy val foo = values.maxBy(_.toString)
println(Enum.foo)
}
// Exiting paste mode, now interpreting.
defined object Enum
scala> Enum main null
foo
scala> Enum.foo
res2: Enum.Value = foo
scala> Enum.values
res3: Enum.ValueSet = Enum.ValueSet(A, foo)
只是为了表明修复类型修复了 SO:
scala> object X extends Enumeration { val x, y, z = new Val ; lazy val f: Any = values.maxBy(_.toString) }
defined object X
scala> X.f
res2: Any = z
我不知道你是如何初始化你的对象的,但我看到的是 WhosebugError
(无论你是否将初始化延迟到 App
):
java.lang.WhosebugError
at java.lang.reflect.Method.getParameterTypes(Method.java:264)
at scala.Enumeration$$anonfun.apply(Enumeration.scala:161)
at scala.Enumeration$$anonfun.apply(Enumeration.scala:161)
at scala.collection.TraversableLike$$anonfun$filterImpl.apply(TraversableLike.scala:259)
at scala.collection.IndexedSeqOptimized$class.foreach(IndexedSeqOptimized.scala:33)
at scala.collection.mutable.ArrayOps$ofRef.foreach(ArrayOps.scala:186)
at scala.collection.TraversableLike$class.filterImpl(TraversableLike.scala:258)
at scala.collection.TraversableLike$class.filter(TraversableLike.scala:270)
at scala.collection.mutable.ArrayOps$ofRef.filter(ArrayOps.scala:186)
at scala.Enumeration.scala$Enumeration$$populateNameMap(Enumeration.scala:161)
at scala.Enumeration$$anonfun$scala$Enumeration$$nameOf.apply(Enumeration.scala:180)
at scala.Enumeration$$anonfun$scala$Enumeration$$nameOf.apply(Enumeration.scala:180)
at scala.collection.MapLike$class.getOrElse(MapLike.scala:128)
at scala.collection.AbstractMap.getOrElse(Map.scala:59)
at scala.Enumeration.scala$Enumeration$$nameOf(Enumeration.scala:180)
at scala.Enumeration$Val.toString(Enumeration.scala:223)
at Enum$$anonfun$foo.apply(<console>:55)
at Enum$$anonfun$foo.apply(<console>:55)
at scala.collection.TraversableOnce$$anonfun$maxBy.apply(TraversableOnce.scala:241)
at scala.collection.TraversableOnce$$anonfun$maxBy.apply(TraversableOnce.scala:240)
at scala.collection.Iterator$class.foreach(Iterator.scala:742)
at scala.collection.AbstractIterator.foreach(Iterator.scala:1194)
at scala.collection.IterableLike$class.foreach(IterableLike.scala:72)
at scala.collection.AbstractIterable.foreach(Iterable.scala:54)
at scala.collection.TraversableOnce$class.maxBy(TraversableOnce.scala:240)
at scala.collection.AbstractTraversable.maxBy(Traversable.scala:104)
at Enum$.foo$lzycompute(<console>:55)
at Enum$.foo(<console>:55)
at sun.reflect.GeneratedMethodAccessor66.invoke(Unknown Source)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
at java.lang.reflect.Method.invoke(Method.java:497)
at scala.Enumeration$$anonfun$scala$Enumeration$$populateNameMap.apply(Enumeration.scala:168)
at scala.Enumeration$$anonfun$scala$Enumeration$$populateNameMap.apply(Enumeration.scala:165)
...
原因很可怕 source code 是在做一些肮脏的反射启发式算法。基本上 populateNameMap
在枚举的主体中查找类型 Value
的值。由于 lazy val foo
的类型 Value
包含在 populateNameMap
的主体中。所以你在这里有一个无限循环。
当您从 lazy val
更改为 def
时,populateNameMap
不再包含 foo
,因为它不是 ValDef
。