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

How does scala.Enumeration.nextName get the identifier text?

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)

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

nextNameOrNull 即使对于原始 Scala 也不再有效 - 因为将一系列名称传递给构造函数已被弃用和删除。 这是使用原始 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

In 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 中都死了——它总是 returns null。

在 Scala JVM 中,如果 nextNameOrNull returns null,Enumeration uses reflection 获取分配给 Value 的 val 的名称。

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

如果你想实现一些知道分配给它的 val 名称的方法,请查看 sbt 的 project macro。 Scala 的枚举可以从 2.10 开始以这种方式实现,但更旧。