Scala case类伴生对象自定义apply方法报错:缺少id参数
问题
尝试在Scala的case类User的伴生对象中自定义apply方法,通过AtomicLong自动生成ID,无需手动传入ID来创建User实例,但调用时出现报错:
missing argument for parameter id of method apply in object User: (id: Long, email: String, login: String, firstname: String, lastname: String, password: String): User
相关代码如下:
case类与伴生对象
case class User private(id: Long, email: String, login: String, firstname: String, lastname: String, password: String) object User { private val seq = new AtomicLong def apply(email: String, login: String, firstname: String, lastname: String, password: String): User = { new User(seq.incrementAndGet(), email, login, firstname, lastname, password) } def mapperTo(id: Long, email: String, login: String, firstname: String, lastname: String, password: String): User = { new User(id, email, login, firstname, lastname, password) } }
控制器调用代码
def createUser = Action { implicit request: MessagesRequest[AnyContent] => //lazy val usersTable = TableQuery[UserTable] val db = Database.forConfig("sqlite") val errorFunction = { (formWithErrors: Form[Data]) => formWithErrors.errors.foreach { error => println(s"Erreur dans le champ : ${error.key}, message : ${error.message}") } BadRequest(views.html.signup(formWithErrors)) } val successFunction = { (data: Data) => // 表单解析成功生成Data对象的场景 val user = User.apply(email = data.email, login = data.login, firstname = data.firstname, lastname = data.lastname, password = data.password) val insertQuery = usersTable += user UserDatabase.db.run(insertQuery) Redirect(routes.HomeController.index()).flashing("info" -> "New user") } val formValidationResult = form.bindFromRequest() formValidationResult.fold(errorFunction, successFunction) }
希望使用自动生成ID的apply方法,但Scala仍要求传入id参数,mapperTo方法用于数据库映射,不应影响apply方法,请问报错原因及解决方法?
原因分析
Scala的case类会自动生成伴生对象的apply方法,这个自动生成的方法参数列表和case类的主构造器完全一致(包含id: Long参数)。当在自定义伴生对象中添加另一个apply方法后,使用命名参数调用时,Scala编译器会优先匹配自动生成的apply方法——因为它的参数列表包含id,而调用时未传递该命名参数,因此触发参数缺失报错。
本质是编译器未选中自定义的无id参数的apply,反而匹配了case类自动生成的带id的版本。
解决方案
有两种可行的解决方式:
方式一:显式覆盖自动生成的apply方法
在伴生对象中显式定义与case类自动生成版本签名完全一致的apply方法,覆盖默认实现,确保编译器能根据参数匹配到正确的版本:
object User { private val seq = new AtomicLong // 自定义无id参数的apply,自动生成ID def apply(email: String, login: String, firstname: String, lastname: String, password: String): User = { new User(seq.incrementAndGet(), email, login, firstname, lastname, password) } // 显式定义带id参数的apply,覆盖case类自动生成的版本 def apply(id: Long, email: String, login: String, firstname: String, lastname: String, password: String): User = { mapperTo(id, email, login, firstname, lastname, password) } def mapperTo(id: Long, email: String, login: String, firstname: String, lastname: String, password: String): User = { new User(id, email, login, firstname, lastname, password) } }
修改后,编译器会根据传入的参数数量或命名选择对应的apply方法,调用不带id的版本时不再报错。
方式二:调用时改用位置参数
如果不想修改伴生对象,可以在创建User时不使用命名参数,直接按自定义apply的参数顺序传值:
val user = User(data.email, data.login, data.firstname, data.lastname, data.password)
这种情况下,编译器会匹配到参数数量为5的自定义apply方法,而非自动生成的6参数版本。
内容的提问来源于stack exchange,提问作者N7Legend
相关产品推荐
相关产品推荐

