使用 Scala 的宏生成一个函数来实例化一个带有默认值的案例 class

Generate a function to instantiate a case class with default values using Scala's macro

有一个案例class是这样的:

case class User(
  id: Long,
  name: String,
  email: String)

我想使用 Scala 宏生成如下函数:

def makeUser(
    id: Long = 1L,
    name: String = "some name",
    email: String = "some email"): User = {
  User(
    id = id,
    name = name,
    email = email)
}

它很冗长,Scala 宏可以解决这个冗长(我希望)。我不太关心默认值;它们可以是随机值。

不知道有没有人能给我一个代码示例。谢谢。

编辑:我想更清楚地说明我想要什么。该函数将仅在单元测试中使用,因此我想避免在我的案例 classes 中乱扔默认值。 (感谢@Tyler 指出)。

Edit2:另外,我想了解更多关于 Scala 的宏。因此,如果有一个宏示例可以实现这一点,对我来说将是一个很好的教训。

您不需要宏,只需向您的伴随对象添加一个新函数(或创建另一个构造函数):

case class User(id: Long, name: String, email: String)
object User {
  def defaultUser(): User = {
    User(1L, "name", "email")
  }
}

然后使用它:

val user = User.defaultUser()

编辑:

正如@jwvh 所指出的,您可以将默认参数直接放入您的案例中 class:

case class User(id: Long = 1L, name: String = "tyler", email: String = "tyler@email.com")
val a = User()
val b = User(email = "new@email.com")

但是,正如我在评论中提到的,因为看起来您正在创建此对象的虚拟实例(可能是为了测试?),我更喜欢像我原来的回答那样将它分开。

我会使用 scalacheck-shapeless 库,但由于您正在研究练习一些代码,这里是使用 typeclass 的示例,它允许您将 class 定义及其定义分开默认值。

trait Default[T] {
  def get: T
}

object Default {
  def apply[T](implicit ev: Default[T]): Default[T] = ev
  implicit val user: Default[User] = new Default[User] {
    def get: User = User(1,"name","email")
  }
}

object Example {
  val user = Default[User]().get
}

编辑:对于评论中的问题。我不会使用以下内容,我将其记下来纯粹是为了演示目的。在这一点上,我会使用 scalacheck-shapeless。然而,这已经接近于我假设的 scalacheck 的实现。我认为 Scalacheck 也会带来一些随机性。下面有很多初始样板,但它适用于任何只包含 Int 和字符串的情况 class(尽管你可以扩展它)。

import shapeless._
trait Default[T] {
  def get: T
}

object Default {
  def apply[T](implicit ev: Default[T]): Default[T] = ev
  private def make[T](t: T): Default[T] = new Default[T] {
    def get: T = t
  }
  implicit val int: Default[Int] = make(1)
  implicit val string: Default[String] = make("some string")
  implicit hnil: Default[HNil] = make(HNil)
  implicit hlist[H, T <: HList](implicit hEv: Default[H], tEv: Default[T]): Default[H :: T] = new Default[H :: T] {
    def make: H :: T = hEv.get :: tEv.ger
  }
  implicit gen[CC, R](implicit gen: Generic.Aux[CC, R], rEv: Default[R]): Default[CC] = {
    gen.from(rEv.get)
  }
}

最终,我决定使用 Scala/Java 反射从案例 类 生成测试数据。

主要优点是 (1) 编译速度更快(因为它不使用宏)和 (2) API 比 scalacheck-shapeless 更好。

操作方法如下:https://give.engineering/2018/08/24/instantiate-case-class-with-arbitrary-value.html