2015-01-07 128 views
2

我的「動態實例case類」一個夢想 - 並提供根據每個字段類型的字段一些虛擬數據(我將創建以後的一些規則)斯卡拉2.10:動態實例case類

到目前爲止,我有一些代碼,以案例的類作品與String; LongInt ......和我有點憋屈,如果它能夠處理嵌入case類

所以我可以實例case class RequiredAPIResponse (stringValue: String, longValue: Long, intVlaue: Int)

而不是外;其中,外是...

case class Inner (deep: String) 
case class Outer (in : Inner) 

的代碼是

def fill[T <: Object]()(implicit mf: ClassTag[T]) : T = { 

     val declaredConstructors = mf.runtimeClass.getDeclaredConstructors 
     if (declaredConstructors.length != 1) 
     Logger.error(/*T.toString + */" has " + declaredConstructors.length + " constructors --- only 1 currently supported.") 
     val constructor = declaredConstructors.headOption.get 

     val m = constructor.getParameterTypes.map(p => { 
      Logger.info("getName " + p.getName +" --- getCanonicalName " + p.getCanonicalName) 
      Logger.info(p.getCanonicalName) 

     p.getCanonicalName match { 
      case "java.lang.String" => /*"Name"->*/ val s : java.lang.String = "DEFAULT STRING" 
      s 
      case "long" => /*"Name"-> */ val l : java.lang.Long = new java.lang.Long(99) 
      l 
      case "int" => /*"Name"->*/ val i : java.lang.Integer = new java.lang.Integer(99) 
      i 
      case _ => /*"Name"->*/ 

      So around here I am stuck! 
     //THIS IS MADE UP :) But I want to get the "Type" and recursively call fill  
      //fill[p # Type] <- not real scala code 

      //I can get it to work in a hard coded manner 
      //fill[Inner] 

     } 
     }) 

我覺得像Scala: How to invoke method with type parameter and manifest without knowing the type at compile time?最後的答案是答案的起點。 因此,而不是使用T- <:對象;填充應採取ClassTag或TypeTag? How can I transform a Map to a case class in Scala? - -

此代碼開始,其中提到(如電梯框架一樣)我確實有liftweb源代碼;但迄今爲止在解決所有祕密方面並未取得成功。

編輯---基於交互的點我有下面的代碼工作(一些小的更新,他的回答)

def fillInner(cls: Class[_]) : Object = { 
    val declaredConstructors = cls.getDeclaredConstructors 
    if (declaredConstructors.length != 1) 
     Logger.error(/*T.toString + */ " has " + declaredConstructors.length + " constructors --- only 1 currently supported.") 
    val constructor = declaredConstructors.headOption.get 

    val m = constructor.getParameterTypes.map(p => { 
     Logger.info("getName " + p.getName + " --- getCanonicalName " + p.getCanonicalName) 
     Logger.info(p.getCanonicalName) 

     p.getCanonicalName match { 
     case "java.lang.String" => /*"Name"->*/ val s: java.lang.String = "DEFAULT STRING" 
      s 
     case "long" => /*"Name"-> */ val l: java.lang.Long = new java.lang.Long(99) 
      l 
     case "int" => /*"Name"->*/ val i: java.lang.Integer = new java.lang.Integer(99) 
      i 
     case _ => fillInner(p) 
     } 

    }) 

    constructor.newInstance(m: _*).asInstanceOf[Object] 

    } 

    def fill[T](implicit mf: ClassTag[T]) : T = fillInner(mf.runtimeClass).asInstanceOf[T] 

感謝, 布倫特

+0

大概ScalaTest或ScalaCheck有這樣的工作了自動生成測試用例。無形肯定是一個選項(從答案似乎凱馬也可能是):http://stackoverflow.com/questions/13402378/generically-rewriting-scala-case-classes –

回答

2

你實際上並沒有使用在ClassTag,只是Class[_],並沒有什麼是類型安全的(它只是Java反射),所以只是通過Class[_]遞歸:

def fillInner(cls: Class[_]) : Any = { 
    val declaredConstructors = cls.getDeclaredConstructors 
    if (declaredConstructors.length != 1) 
    Logger.error(/*T.toString + */" has " + declaredConstructors.length + " constructors --- only 1 currently supported.") 
    val constructor = declaredConstructors.headOption.get 

    val m = constructor.getParameterTypes.map(p => { 
     Logger.info("getName " + p.getName +" --- getCanonicalName " + p.getCanonicalName) 
     Logger.info(p.getCanonicalName) 

    p.getCanonicalName match { 
     case "java.lang.String" => /*"Name"->*/ val s : java.lang.String = "DEFAULT STRING" 
     s 
     case "long" => /*"Name"-> */ val l : java.lang.Long = new java.lang.Long(99) 
     l 
     case "int" => /*"Name"->*/ val i : java.lang.Integer = new java.lang.Integer(99) 
     i 
     case _ => fillInner(p) 
    } 
    }) 

def fill[T: ClassTag]: T = fillInner(classOf[T].runtimeClass).asInstanceOf[T] 

但你也許可以完成你想要的類型安全的方式做,比如用無形的:通過隱分辨率的神奇

trait Supplier[T] { 
    def supply: T 
} 
object Supplier[T] { 
    implicit val intSupplier = new Supplier[Int] { 
    def supply = 99 
    } 
    implicit val stringSupplier = ... 
    implicit val emptyHListSupplier = new Supplier[HNil] { 
    def supply = HNil 
    } 
    implicit def consHListSupplier[H, T <: HList](
    implicit headSupplier: Supplier[H], 
     tailSupplier: Supplier[T]) = new Supplier[H :: T] { 
    def supply = headSupplier.supply :: tailSupplier.supply 
    } 
} 

然後你就可以得到一個Supplier[(String :: HNil) :: Int :: HNil]或等任何遞歸HListHList小號最終只包含你已經獲得的價值;你只需要多一點不成形(版本1或2不同,它已經有一段時間,因爲我已經做到了,所以我不記得細節)來回那些和case類之間的轉換。

+0

輝煌的感謝 - 你對不需要ClassTag在fillInner點讓我度過了!一旦我的大腦停止感覺如此糊塗,我會看起來沒有形狀 – brent