2017-08-11 73 views
0

在Scala中,是否有访问类成员的一种方式,但成员名称是一个变种? 下面是一个代码片段,有一个叫做“one_star”的类成员。我有一个值为“one_star”的var,我想用这个var作为“case class”的成员名称。如何使用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", 
    var five_star: String = "0" 

    // other about 
) 

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

回答

1

如果要动态改变字段名,即提供类变量名作为值,找到匹配给定的变量名和最后更改值该字段场,有几种方法:简单的一个是使用模式匹配来检查字段值并自己更改实例值并返回实例。然而,如果你需要处理在你的类中定义的每个字段,并且在你有很多字段的情况下,它可能非常麻烦,这可能非常麻烦。因此,你需要一些通用的方法来解决这个问题。

另一种方法是使用阶反射。 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为在提供无效的变量名和值的情况。另外,如果您的案例类包含> 22个字段参数,则案例类的某些功能不可用。

+0

非常感谢您的长期回答,这非常符合我的要求。我搜索,经过斯卡拉2.11 – xuhai

+0

是,Scala的支持从2.11版> 22场“你们班22场”的限制已取消,我想要说的是有一些限制,如案例类将不再有不应用或unapplyseq和tupled(你将不再转换情况下类数组)的功能,因为Scala仍然不支持元组多于22个值。顺便说一句,如果您不想要上述功能,您仍然可以定义大于22个字段的案例类。 –

0

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 
    } 
    } 
} 
+0

谢谢,您setField功能对我来说很好。但也有在案件类40余名,所以有一个更优雅的方式 – xuhai

0

这是可能的使用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") 

您确定不想为您的班级使用或扩展Map[String, String]吗?这么多房产并不典型。

+0

谢谢您的回答!我是斯卡拉新手。我有一个配置单元的数据框,其中一列是一个结构。我测试过一个“case class”variate可以改成struct的列,但是这个结构有超过40个成员。地图[字符串,字符串]可以做到这一点? – xuhai

相关问题