scala case class 這時候該怎麼用?

case class data(a: Int, b: String, c: String, d: Int, e: Int)

val arr =line.split(" ")

data(arr(0), arr(1), arr(2), arr(3), arr(4))

// 目前項目中的表非常大,如果array裡面有幾十個那該怎麼寫,請問大神們有沒有更好的辦法啊?


為了回答樓主的新問題,我特意給 shapeless 發了個 Pull Request #598,現在已經合併到 shapeless 2.3.2中。

用我這個Pull Request的話,大概可以這樣寫:

case class data(a: Int, b: String, c: String, d: Int, e: Int)

val arr = line.split(" ")

val i = arr.iterator

import shapeless._
object readField extends shapeless.Poly0 {
implicit def readInt = at[Int](i.next.toInt)
implicit def readString = at[String](i.next)
}
Generic[data].from(HList.fillWith[the.`Generic[data]`.Repr](readField))

----------------------------------------

原問題:

case class data(a: Int, b: Int, c: Int, d: Int, e: Int)

val arr = line.split(" ")

data(arr(0), arr(1), arr(2), arr(3), arr(4))

// 目前項目中的表非常大,如果array裡面有幾十個那該怎麼寫,請問大神們有沒有更好的辦法啊?

原答案:

黑科技來了:

case class data(a: Int, b: Int, c: Int, d: Int, e: Int)

val arr = "1 2 3 4 5".split(" ")

val myData = arr.foldLeft[Any](data.curried) { (f, s) =&>
f.asInstanceOf[Int =&> Any](s.toInt)
}.asInstanceOf[data]

注意,本代碼純屬娛樂,寫得很污,請勿模仿!


OK,讓我們一勞永逸的解決這個問題吧,讓大家見識一下Scala的強大

給定一個字元串,自動轉換為需要的case class

比如:

  1. 給定字元串"zhangsan,100,34",定義了case class Person(name: String, id: Int, age: Int),就能自動由字元串得到Person(zhangsan, 100, 34)

  2. 給定字元串"100,300,400",定義了case class Vec(x: Int, y: Int, z: Int),就能自動由字元串得到Vec(100, 300, 400)

首先給出最終的實現效果:

@ case class Person(name: String, id: Int, age: Int)
defined class Person
@ Str2Class[Person]("ZhangSan,888,33").get
res3: Person = Person("ZhangSan", 888, 33)
@ case class Vec(x: Int, y: Int, z: Int)
defined class Vec
@ Str2Class[Vec]("100,200,300").get
res5: Vec = Vec(100, 200, 300)

下面是具體的實現代碼:

object Str2Class {
import util.{ Failure, Success, Try }
trait Conveter[A] { def convert(text: String): Try[A] }
object Conveter {
def apply[A](implicit c: Conveter[A]): Conveter[A] = c
implicit object s2s extends Conveter[String] {
def convert(text: String) = Try(text)
}
implicit object s2i extends Conveter[Int] {
def convert(text: String) = Try(text.toInt)
}
}

import shapeless._
trait Parser[R &<: HList] { def parse(xs: Seq[String]): Try[R] } object Parser { def apply[R &<: HList](implicit p: Parser[R]) = p def fromFun[R &<: HList](f: Seq[String] =&> Try[R]): Parser[R] = new Parser[R] {
def parse(xs: Seq[String]) = f(xs)
}
implicit object HNilParser extends Parser[HNil] {
def parse(xs: Seq[String]): Try[HNil] = xs match {
case Seq() =&> Success(HNil)
case _ =&> Failure(new RuntimeException("More items than expected."))
}
}
implicit def hListParser[A: Conveter, R &<: HList : Parser]: Parser[A :: R] = fromFun { case x +: rs =&> for(xv &<- Conveter[A].convert(x);rv &<- Parser[R].parse(rs)) yield xv::rv case Seq() =&> Failure(new RuntimeException("Less items than expected."))
}
}

trait LineParser[A] {
def apply[R &<: HList](text: String) (implicit gen: Generic.Aux[A, R], p: Parser[R]): Try[A] = p.parse(text.split(",")) map (gen.from) } def apply[A] = new LineParser[A]{} }

==================原回答===================

Here we go:

@ import shapeless._
@ import syntax.std.traversable._
@ import syntax.std.tuple._

@ case class data(a: Int, b: Int, c: Int, d: Int, e: Int)
defined class data
@ type DATA = Int :: Int :: Int :: Int :: Int :: HNil
defined type DATA
@ val arr = "1 2 3 4 5".split(" ").map(_.toInt)
arr: Array[Int] = Array(1, 2, 3, 4, 5)

@ val myData = data.tupled(arr.toHList[DATA].get.tupled)
myData: data = data(1, 2, 3, 4, 5)


https://issues.scala-lang.org/browse/SI-7296

limit of 22 是因為apply和unapply的參數個數限制(FunctionN),不知道你怎麼使用case class,不需要pattern matching的話,直接用constructor,不要用case class了;

否則的話按照 @neo lin的做法(嵌套)去restructure,或換版本。

[雖然用implicit arguments 可以減少fields,但是這是坑不要踩]

------------------------------------------

一般的ORM框架對injection data的做法就是用反射了

case class data(a: Int, b: Int, c: Int, d: Int, e: Int)
val arr = "1 2 3 4 5".split(" ").map(_.toInt)

//調用constructor
val ru = scala.reflect.runtime.universe
val dataConstr = ru.runtimeMirror(
getClass.getClassLoader
).reflectClass(
ru.typeOf[data].typeSymbol.asClass
).reflectConstructor(
ru.typeOf[data].declaration(ru.nme.CONSTRUCTOR).asMethod
)
println(dataConstr(arr: _*))

//or 調用apply方法
val m = ru.runtimeMirror(getClass.getClassLoader)
val methodApply = m.reflect(
m.reflectModule(ru.typeOf[data.type].termSymbol.asModule).instance
).reflectMethod(
ru.typeOf[data.type].declaration(ru.newTermName("apply")).asMethod
)
println(methodApply(arr: _*))


忘了現在最新版有沒有22個值的上限。

用嵌套,或者合理劃分你的類型。

題主改問題了,請參考HList的那套答案。瀉藥了。


推薦閱讀:

代數數據類型是什麼?
kotlin和scala兩種語言的對比?
如何看待TIOBE2016年預測scala將停留在前20內?
模式匹配是語法糖嗎?
如何評價scalaz這個庫?

TAG:Scala | 數據結構 | 元編程 | 類型系統 | 文本分析 |