Skip to main content
 首页 » 编程设计

scala之从字符串构造简单的 Scala 案例类,严格没有样板

2024年10月01日25exmyth

我寻求简洁的代码来从字符串(例如 csv 行)初始化简单的 Scala 案例类:

case class Person(name: String, age: Double) 
case class Book(title: String, author: String, year: Int) 
case class Country(name: String, population: Int, area: Double) 
 
val amy = Creator.create[Person]("Amy,54.2") 
val fred = Creator.create[Person]("Fred,23") 
val hamlet = Creator.create[Book]("Hamlet,Shakespeare,1600") 
val finland = Creator.create[Country]("Finland,4500000,338424") 

什么是最简单的 Creator反对这样做?通过看到一个很好的解决方案,我会学到很多关于 Scala 的知识。

(注意伴随对象 PersonBookCountry 不应该被强制存在。那将是样板!)

请您参考如下方法:

我将使用 Shapeless 给出一个解决方案,该解决方案在给定一些关于类型安全性的合理约束(没有运行时异常、没有运行时反射等)的情况下尽可能简单。对于泛型推导:

import scala.util.Try 
import shapeless._ 
 
trait Creator[A] { def apply(s: String): Option[A] } 
 
object Creator { 
  def create[A](s: String)(implicit c: Creator[A]): Option[A] = c(s) 
 
  def instance[A](parse: String => Option[A]): Creator[A] = new Creator[A] { 
    def apply(s: String): Option[A] = parse(s) 
  } 
 
  implicit val stringCreate: Creator[String] = instance(Some(_)) 
  implicit val intCreate: Creator[Int] = instance(s => Try(s.toInt).toOption) 
  implicit val doubleCreate: Creator[Double] = 
    instance(s => Try(s.toDouble).toOption) 
 
  implicit val hnilCreator: Creator[HNil] = 
    instance(s => if (s.isEmpty) Some(HNil) else None) 
 
  private[this] val NextCell = "^([^,]+)(?:,(.+))?$".r 
 
  implicit def hconsCreate[H: Creator, T <: HList: Creator]: Creator[H :: T] = 
    instance { 
      case NextCell(cell, rest) => for { 
        h <- create[H](cell) 
        t <- create[T](Option(rest).getOrElse("")) 
      } yield h :: t 
      case _ => None 
    } 
 
  implicit def caseClassCreate[C, R <: HList](implicit 
    gen: Generic.Aux[C, R], 
    rc: Creator[R] 
  ): Creator[C] = instance(s => rc(s).map(gen.from)) 
} 

这完全按照指定的方式工作(但请注意,这些值包含在 Option 中以表示解析操作可能失败的事实):
scala> case class Person(name: String, age: Double) 
defined class Person 
 
scala> case class Book(title: String, author: String, year: Int) 
defined class Book 
 
scala> case class Country(name: String, population: Int, area: Double) 
defined class Country 
 
scala> val amy = Creator.create[Person]("Amy,54.2") 
amy: Option[Person] = Some(Person(Amy,54.2)) 
 
scala> val fred = Creator.create[Person]("Fred,23") 
fred: Option[Person] = Some(Person(Fred,23.0)) 
 
scala> val hamlet = Creator.create[Book]("Hamlet,Shakespeare,1600") 
hamlet: Option[Book] = Some(Book(Hamlet,Shakespeare,1600)) 
 
scala> val finland = Creator.create[Country]("Finland,4500000,338424") 
finland: Option[Country] = Some(Country(Finland,4500000,338424.0)) 
Creator这是一个类型类,它提供了我们可以将字符串解析为给定类型的证据。我们必须为基本类型提供显式实例,例如 String , Int等,但我们可以使用 Shapeless 来泛型派生案例类的实例(假设我们有 Creator 实例用于它们的所有成员类型)。