如何使用 scala class 成员名称作为变量

How to use scala class member name as variable

在 scala 中,有没有办法访问 class 成员,但成员名称是 var? 下面是一个代码片段,有一个名为 "one_star" 的 class 成员。我有一个变量,其值为 "one_star",我想将此变量用作 "case class".

的成员名称
case class Features(
  // star about
  var one_star: String = "0",
  var two_star: String = "0",
  var three_star: String = "0",
  var four_star: String = "0",
  var five_star: String = "0"

  // other about
)

object Features {
  def apply(): Features = {
    val features = new Features()
    var testVar = "one_star"
    features.${testVar} = "1"
    features
  }
}

Scala 是静态类型语言,不允许这些语言结构。但是你可以使用反射(硬方式)或模式匹配这样的代码(简单方式):

class Features (
  var one_star: String = "0",
  var two_star: String = "0",
  var three_star: String = "0",
  var four_star: String = "0",
  var five_star: String = "0") {

  def setField(field: String, value: String): Unit = {
    field match {
      case "one_star" => one_star = value
      case "two_star" => two_star = value
      case "three_star" => three_star = value
      case "four_star" => four_star = value
      case "five_star" => five_star = value
    }
  }
}

这可以使用 scala-reflect,尽管在大多数情况下我不推荐它。

import scala.reflect.runtime.universe._

val field = typeOf[Features].decl(TermName(testVar)).asTerm.accessed.asTerm
val mirror = runtimeMirror(classOf[Features].getClassLoader)
mirror.reflect(features).reflectField(field).set("1")

您确定不想为您的 class 使用或扩展 Map[String, String] 吗?这么多属性不典型。

如果您想动态更改字段名称,即提供 class 变量名作为值,找到匹配给定变量名的字段,最后更改该字段的值 ,有几种方法:简单的一种是使用模式匹配自行检查字段值并更改实例值和 return 实例。但是,它可能会非常混乱,因为您需要处理 class 中定义的每个字段,如果您有很多字段,它可能会非常麻烦。因此,您将需要一些通用的方法来解决这个问题。

另一种方法是使用 scala 反射。 Scala 反射是为此而设计的,即在运行时像您的情况一样以更通用的方式修改您的代码。以下是更改给定字段名称的实例值的代码片段。

  case class Features(
                       // star about
                       var one_star: String = "0",
                       var two_star: String = "0",
                       var three_star: String = "0",
                       var four_star: String = "0"

                       // other about
                     ) {
    def copyInstance(variableName: String, value: String): Features = {
      // 1. Initialize Features instance
      val instance = Features()

      // 2. Import scala reflection api.
      import scala.reflect.runtime.{universe => ru}

      // 3. Get the mirror of instance of Features class.
      // Mirror will reflect to instance of Features case class, and we will use this instance mirror to modify its fields value.
      val instanceMirror = ru.runtimeMirror(instance.getClass.getClassLoader).reflect(instance)

      // 4. Now, Get the field whose value need to be changed - i.e. name you provide - variableName.
      val field = ru.typeOf[Features].decl(ru.TermName(variableName)).asTerm

      // 5. Get the mirror for that field so that we can read and write to this field.
      val fieldMirror = instanceMirror.reflectField(field)

      // 6. Finally, set the value to this field.
      fieldMirror.set(value)

      // 7. Return the changed instance.
      instance
    }
  }

  val features = Features()
  val changedFeatures = features.copyInstance("one_star", "changed")

  println(features)
  println(changedFeatures)

//Result
Features(0,0,0,0)
Features(changed,0,0,0)

另请注意,对于提供了无效变量名称和值的情况,您可能需要处理 Exception。此外,如果您的案例 class 包含 >22 个字段参数,则案例 class 的某些功能将不可用。