【问题标题】:How does scala.Enumeration.nextName get the identifier text?scala.Enumeration.nextName 如何获取标识符文本?
【发布时间】:2020-01-11 17:29:30
【问题描述】:

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)

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

【问题讨论】:

  • 总结:引用的枚举代码实际上并没有实现这一点(始终返回 null),如果您更深入地遵循调用链,则会使用反射。使用反射(a la Enumeration)或宏(请参阅所选答案中的链接)以获得相同的效果。

标签: scala scala.js


【解决方案1】:

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

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

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

【讨论】:

  • 你不能在具体的枚举实现中覆盖nextName 吗?在 Scala.js 中,这是可行的 (test case)。
  • 当然,您甚至可以将迭代器直接分配给它,但根据2.10 deprecation docs“名称应单独指定或通过反射发现” - 这不是一个好主意(而且绝对不是正常方式),似乎他们只是忘记删除它(也许是为了反向兼容)。
  • 您可以覆盖Value() = Val(nextNameOrNull)(如在OP的示例中)-您将收到Val(null),或者您也可以使用Val(myIterator.next)覆盖它并接收预期值(不需要@ 987654334@)。所以这个类的封装还有很多不足之处。
【解决方案2】:

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

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。

【讨论】:

    猜你喜欢
    • 2021-02-26
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2019-08-02
    • 2021-05-19
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多