如何修改Wrapper.apply适配Mapper实例?延迟类型推断技术问题
首先,咱们先搞清楚原代码为什么编译失败:
在Wrapper的apply方法里,你写了f(this),但编译器完全不知道U是什么类型——它可能是字符串、数字,或者任何东西,自然没法确定f能接收Wrapper[T]作为参数并被调用。咱们的目标是让Wrapper实例能接收Mapper实例,本质上就是要让f必须是一个能处理当前Wrapper[T]的映射器类型,所以得给U加上合适的类型约束。
下面是几种可行的解决方案:
方案一:直接指定参数类型为Mapper(最直接)
如果你的需求就是让Wrapper只接收Mapper实例,那直接把apply的参数类型定为Mapper就好,简单粗暴还类型安全:
trait Wrapper[T] { // 明确接收Mapper实例,返回Option[T](编译器会自动推断Mapper.apply的V=T) def apply(f: Mapper): Option[T] = f(this) } trait Mapper { def apply[V](w: Wrapper[V]): Option[V] }
这样调用的时候,直接传Mapper实例就行:
val myWrapper: Wrapper[Int] = new Wrapper[Int] {} val myMapper: Mapper = new Mapper { override def apply[V](w: Wrapper[V]): Option[V] = Some(/* 你的映射逻辑 */) } val result: Option[Int] = myWrapper(myMapper)
优点:完全没有类型擦除或反射的问题,代码清晰,编译器能做完整的类型检查。
方案二:用结构类型支持更通用的映射器
如果希望Wrapper不仅支持Mapper,还支持其他具有相似apply方法的类型,可以用Scala的结构类型来约束U:
import scala.language.reflectiveCalls // 需要启用这个语言特性 trait Wrapper[T] { // 约束U必须有一个接收Wrapper[T]的apply方法,返回任意类型R def apply[R](f: { def apply(w: Wrapper[T]): R }): R = f(this) } trait Mapper { def apply[V](w: Wrapper[V]): Option[V] }
这里结构类型{ def apply(w: Wrapper[T]): R }会匹配任何拥有对应apply方法的类型,Mapper自然符合这个要求——当你传入Mapper实例时,编译器会自动匹配V=T的情况,返回Option[T]。
优点:灵活性高,支持多种映射器类型;缺点:结构类型依赖反射,有轻微性能开销,需要启用reflectiveCalls语言特性。
方案三:用函数类型实现最高灵活性
如果想让Wrapper支持任何能处理Wrapper[T]的函数,包括把Mapper转换成函数来使用,可以把apply的参数定义为函数类型:
trait Wrapper[T] { // 接收一个从Wrapper[T]到R的函数,返回R def apply[R](f: Wrapper[T] => R): R = f(this) } trait Mapper { def apply[V](w: Wrapper[V]): Option[V] }
调用的时候,需要把Mapper的apply方法转换成函数:
val result: Option[Int] = myWrapper(w => myMapper(w)) // 或者更简洁的写法:myWrapper(myMapper.apply)
优点:最通用,支持任何函数类型;缺点:需要手动转换Mapper为函数,稍微多写一点代码。
总结一下:如果你的场景只需要支持Mapper,方案一最适合;如果需要支持多种类似类型,方案二或三可以根据性能和灵活性需求选择。
内容的提问来源于stack exchange,提问作者Adam

