Scala泛型包装类实现时Int类型+方法调用报错及无缝调用咨询
问题原因
- Scala编译器在找不到目标类型的对应
+方法重载时,会触发Predef中定义的any2stringadd隐式转换,该转换会把任意类型转为支持字符串拼接的对象,仅接受String类型作为+的参数,这就是你看到要求传入String的报错的根因。 Dynamic特质的applyDynamic触发有前提:编译器无法在静态类型中找到匹配的方法签名才会fallback到动态调用。你当前的Wrapper类没有显式声明任何操作符方法,同时原始类型Int的+方法本身是Scala编译器的语法糖,本质是编译期内联的算术操作,无法通过反射从Int.getClass中拿到对应的方法,所以你的动态调用逻辑就算触发了也无法正常执行。
实现方案
方案1:显式重载常用操作符(适配数值类型场景,性能更高)
如果主要是针对数值类型做包装追踪,可以直接在Wrapper类中显式重载对应操作符,同时配合隐式转换支持原生类型和包装类型的互操作:
import scala.language.implicitConversions case class Wrapper[T](value: T) extends Dynamic { // 显式重载Int类型的+操作,可按需扩展其他数值类型、其他操作符 def +(other: Wrapper[Int])(implicit ev: T =:= Int): Wrapper[Int] = { val res = ev(this.value) + other.value // 这里可以插入值变更事件触发逻辑 Wrapper(res) } def +(other: Int)(implicit ev: T =:= Int): Wrapper[Int] = { val res = ev(this.value) + other // 这里可以插入值变更事件触发逻辑 Wrapper(res) } // 其他Dynamic逻辑保留,处理自定义类的方法调用 def applyDynamic(id: String)(parameters: Any*) = { val objectParameters = parameters.map { case w: Wrapper[_] => w.value.asInstanceOf[Object] case x => x.asInstanceOf[Object] } val parameterClasses = objectParameters.map(_.getClass) val method = value.getClass.getMethod(id, parameterClasses: _*) val res = method.invoke(value, objectParameters: _*) Wrapper(res) } } // 定义隐式转换,支持原生类型 + 包装类型的调用 implicit def intToWrapperOps(i: Int): WrapperIntOps = WrapperIntOps(i) case class WrapperIntOps(i: Int) { def +(other: Wrapper[Int]): Wrapper[Int] = { val res = i + other.value // 这里可以插入值变更事件触发逻辑 Wrapper(res) } }
上述代码就可以满足你给出的三种调用场景:
val a = Wrapper[Int](1) val b = Wrapper[Int](2) val c = 3 a + b // 输出 Wrapper(3) a + c // 输出 Wrapper(4) c + a // 输出 Wrapper(4)
如果需要支持更多类型、更多操作符,按照相同模式扩展即可,也可以用类型类来简化多类型适配的重复代码。
方案2:优化Dynamic调用逻辑(适配任意自定义类型场景)
如果需要包装任意自定义类的方法调用,需要调整动态调用的参数解析逻辑,同时禁用any2stringadd隐式转换避免报错:
import scala.language.dynamics // 禁用默认的字符串拼接隐式转换 import Predef.{any2stringadd => _, _} case class Wrapper[T](value: T) extends Dynamic { def applyDynamic(id: String)(parameters: Any*) = { // 自动解包参数中的Wrapper实例 val rawParams = parameters.map { case w: Wrapper[_] => w.value case x => x } val objectParams = rawParams.map(_.asInstanceOf[Object]) val paramClasses = objectParams.map(_.getClass) val method = value.getClass.getMethod(id, paramClasses: _*) val res = method.invoke(value, objectParams: _*) Wrapper(res) } }
注意该方案对Int等Scala值类型的运算符依然不生效,因为这些运算符不存在于运行时类的方法列表中,仅能用于自定义类的普通方法调用。
内容的提问来源于stack exchange,提问作者0x4950
相关产品推荐
相关产品推荐

