Shapeless Lens在带类型参数的类中编译错误原因及修复
问题:Shapeless MkFieldLens在泛型类中找不到隐式实例的原因与修复
问题场景回顾
你定义了BaseEntity作为所有case类的基类,想用Shapeless Lens修改基类中的createTime字段。直接调用Util.updateCreateTime方法处理具体的User实例时一切正常,但在泛型类BaseRepo[E <: BaseEntity]中调用该方法时,编译器抛出错误:
could not find implicit value for parameter mkLens: shapeless.MkFieldLens.Aux[E,demo.Util.tpeCreateTime,java.sql.Timestamp]
错误原因分析
这背后的核心是编译器在泛型上下文无法自动推导隐式实例:
- 当你直接调用
Util.updateCreateTime(user, now)时,编译器明确知道参数类型是User,它可以自动生成MkFieldLens.Aux[User, tpeCreateTime, Timestamp]这个隐式实例——Shapeless的宏会在编译期为具体的case类生成对应的Lens逻辑。 - 但在
BaseRepo[E <: BaseEntity]中,E是一个抽象的泛型类型(只知道它是BaseEntity的子类),编译器无法提前为所有可能的E类型预先生成隐式实例,也无法确认传入的具体E类型是否满足拥有createTime字段且类型为Timestamp的要求(哪怕BaseEntity定义了这个字段,编译器也无法直接关联到case类的实际字段结构)。
修复方案
我们需要给泛型类BaseRepo添加隐式参数约束,告诉编译器:任何用于BaseRepo的具体类型E,必须存在对应的MkFieldLens.Aux[E, tpeCreateTime, Timestamp]隐式实例。有两种简洁的写法:
写法1:显式添加隐式参数
class BaseRepo[E <: BaseEntity](implicit mkLens: MkFieldLens.Aux[E, Util.tpeCreateTime, Timestamp]) { def beforeUpdate(e: E): E = { val now = new Timestamp(System.currentTimeMillis()) Util.updateCreateTime(e, now) } }
写法2:使用上下文边界(更简洁)
利用Scala的上下文边界语法,把隐式约束整合到泛型参数声明中:
import shapeless.MkFieldLens class BaseRepo[E <: BaseEntity : MkFieldLens.Aux[*, Util.tpeCreateTime, Timestamp]] { def beforeUpdate(e: E): E = { val now = new Timestamp(System.currentTimeMillis()) Util.updateCreateTime(e, now) } }
验证修复效果
修改后,当你创建BaseRepo[User]的实例时,编译器会自动为User生成对应的隐式MkFieldLens实例,代码可以正常运行:
object LensTest extends App { val user = User(1, "Tom", 18, null, null) val now = new Timestamp(System.currentTimeMillis()) def normal_method_call_is_ok(): Unit = { println(Util.updateCreateTime(user, now)) } normal_method_call_is_ok() // 修复后可正常使用 val userRepo = new BaseRepo[User] println(userRepo.beforeUpdate(user)) }
额外说明
如果需要同时处理updateTime字段,你可以用同样的方式添加对应的隐式约束,或者把多个约束整合到一个类型类中,让代码更整洁。
内容的提问来源于stack exchange,提问作者user5698801
相关产品推荐
相关产品推荐

