2013-06-26 80 views
2

對於參數數量爲(21 !!)的斯卡拉案例類別具有動態命名參數的Scala案例類副本

例如, case class Car(type: String, brand: String, door: Int ....) 其中type =吉普車,品牌=豐田,門= 4 ....等

而且還有一個複製方法,其允許超馳與命名參數:Car.copy(brand = Kia) 其中將成爲類型=吉普車,品牌=起亞,門= 2 ...等

我的問題是,有無論如何我可以提供指定的參數動態?

def copyCar(key: String, name: String) = { 
    Car.copy("key" = "name") // this is something I make up and want to see if would work 
} 

scala反射庫可以提供幫助嗎?

我使用複製方法的原因是,我不想每次創建僅有1或2個參數更改的案例類時重複21個參數分配。

非常感謝!

+2

我不明白爲什麼'copy'命令不能完全滿足你的需求。爲什麼要動態提供參數? –

+0

@RexKerr我可以看到它:他得到(name,value)對的映射,然後想用它調用'Car.copy()'。你的方式將全部硬編碼到特定參數。 – javadba

回答

0

是的,你需要使用反射來做到這一點。

這是一個有點牽扯,因爲copy是一種綜合方法,你必須調用除了你想要替換的所有字段的getters。

爲了給你一個想法,copy method in this class確實如此,除了使用參數索引而不是名稱。它調用伴侶對象的方法apply,但效果相同。

0

我有點困惑 - 以下不是你所需要的?

car: Car = ...   // Retrieve an instance of Car somehow. 
car.copy(type = "jeep") // Copied instance, only the type has been changed. 
car.copy(door = 4)  // Copied instance, only the number of doors has changed. 
// ... 

是否因爲您有很多初始實例創建的參數?在這種情況下,你不能使用默認值嗎?

case class Car(type: String = "Jeep", door: Int = 4, ...) 

你似乎知道這些功能,並覺得他們不符合你的需要 - 你能解釋爲什麼嗎?

3

FWIW,我剛剛實現了一個Java反射版本:CaseClassCopy.scala。我試過TypeTag version,但沒有那麼有用; TypeTag對此目的太嚴格了。

def copy(o: AnyRef, vals: (String, Any)*) = { 
    val copier = new Copier(o.getClass) 
    copier(o, vals: _*) 
    } 

    /** 
    * Utility class for providing copying of a designated case class with minimal overhead. 
    */ 
    class Copier(cls: Class[_]) { 
    private val ctor = cls.getConstructors.apply(0) 
    private val getters = cls.getDeclaredFields 
     .filter { 
     f => 
     val m = f.getModifiers 
     Modifier.isPrivate(m) && Modifier.isFinal(m) && !Modifier.isStatic(m) 
    } 
     .take(ctor.getParameterTypes.size) 
     .map(f => cls.getMethod(f.getName)) 

    /** 
    * A reflective, non-generic version of case class copying. 
    */ 
    def apply[T](o: T, vals: (String, Any)*): T = { 
     val byIx = vals.map { 
     case (name, value) => 
      val ix = getters.indexWhere(_.getName == name) 
      if (ix < 0) throw new IllegalArgumentException("Unknown field: " + name) 
      (ix, value.asInstanceOf[Object]) 
     }.toMap 

     val args = (0 until getters.size).map { 
     i => 
      byIx.get(i) 
      .getOrElse(getters(i).invoke(o)) 
     } 
     ctor.newInstance(args: _*).asInstanceOf[T] 
    } 
    } 
+0

這是[略微清理的版本](https://gist.github.com/mslinn/a64b5152b2c035103a326d3f21ce2e79),與Scala 2.12一起使用 –