scala.Enumeration.nextName如何获取标识符文本?

时间:2015-03-31 23:41:17

标签: scala scala.js

scala.Enumerator.nextName.nextNameOrNull目前已阅读:

/** The string to use to name the next created value. */
protected var nextName: Iterator[String] = _

private def nextNameOrNull =
  if (nextName != null && nextName.hasNext) nextName.next() else null
随后调用

nextNameOrNull以获取用于在枚举中创建的项目的名称。

此代码实际上是如何实现此目的的?

当我将其复制粘贴到一个简单的例子中时:

class MyBaseClass extends Serializable {
  /** The string to use to name the next created value. */
  protected var nextName: Iterator[String] = _

  private def nextNameOrNull =
    if (nextName != null && nextName.hasNext) nextName.next() else null

  protected final def Value(): Val = Val(nextNameOrNull)

  case class Val(name:String)
}

object MyObject extends MyBaseClass {
  val myValue = Value

  println("Hello from MyObject, myValue: " + myValue)
}

它打印:Hello from MyObject, myValue: Val(null)而不是Val(myValue)

的希望

我需要添加什么才能使其正常工作?

2 个答案:

答案 0 :(得分:4)

在Scala JVM中,Enumeration uses reflection获取Value返回null时已分配nextNameOrNull的val的名称。

在Scala.js中,我们没有这种奢侈(没有反射支持)。因此,Scala.js编译器特殊情况 scala.Enumeration,以便使用它的代码可以工作。

如果你想实现一些知道它所分配的val名称的mdethod,请看看sbt的project macro。 Scala的Enumerations可以从2.10开始实现,但是更老。

答案 1 :(得分:2)

即使对于原始Scala,

nextNameOrNull也不再有效 - 因为不推荐使用将一系列名称传递给构造函数。 这是使用原始scala的Enumeration(不是scala-js中替换的那个)的2.11.2的执行:

scala> object MyObject extends Enumeration {
     |   val MyValue1, MyValue2 = Value
     | 
     |   println("nextName: " + nextName)
     | }
defined object MyObject

scala> MyObject
nextName: null //still null

在构造函数中使用的2.10.x nextName中明确指定名称作为序列(在2.11.x中删除):

@deprecated("Names should be specified individually or discovered via reflection", "2.10.0")
  def this(initial: Int, names: String*) = {
    this(initial)
    this.nextName = names.iterator
  }
}

现在删除了这个构造函数,nextName只是一个死代码。 Scala使用populateNameMap()nameOf提供名称(如果未指定:

private def populateNameMap() {
    val fields = getClass.getDeclaredFields
    def isValDef(m: JMethod) = fields exists (fd => fd.getName == m.getName && fd.getType == m.getReturnType)

    // The list of possible Value methods: 0-args which return a conforming type
    val methods = getClass.getMethods filter (m => m.getParameterTypes.isEmpty &&
                                                   classOf[Value].isAssignableFrom(m.getReturnType) &&
                                                   m.getDeclaringClass != classOf[Enumeration] &&
                                                   isValDef(m))
    methods foreach { m =>
      val name = m.getName
      // invoke method to obtain actual `Value` instance
      val value = m.invoke(this).asInstanceOf[Value]
      // verify that outer points to the correct Enumeration: ticket #3616.
      if (value.outerEnum eq thisenum) {
        val id = Int.unbox(classOf[Val] getMethod "id" invoke value)
        nmap += ((id, name))
      }
    }
  }

所以它默认使用反射。您可以明确指定每个值的名称,如here所述。

我认为ScalaJs也是如此,排除它没有populateNameMap()方法,因为JavaScript没有这种反射 - 所以非显式命名参数的结果是:

override def toString() =
  if (name != null) name //only if you did `Value("explicitName")` somwhere inside enumeration
  // Scala.js specific
  else s"<Unknown name for enum field #$i of class ${getClass}>"

但是再一次,nextNameOrNull在Scala和Scala-Js中都死了 - 它总是返回null。