Scala 2.12.4中类型细化的隐式扩展方法失效问题咨询
咱们直接点出核心问题:你的隐式扩展没有被编译器自动触发,因为toAllOps不是一个标记为implicit的转换方法。这就是为什么显式调用它能正常工作,但直接调用Test[String,Int].foo(2.0)会报错的根本原因。
1. 编译器如何触发隐式扩展?
在Scala里,要让编译器自动给某个类型添加扩展方法,你需要提供隐式转换——要么是标记了implicit的转换方法,要么是隐式类。你的syntax.toAllOps现在只是一个普通方法,编译器没有理由自动调用它来给Test.Aux[A,B]实例附加Ops的方法。
举个简单类比:如果你写了def intToString(i: Int): String = i.toString,编译器不会自动把123变成字符串,除非你给方法加上implicit关键字。你的toAllOps就是类似的情况。
2. 类型细化(Aux类型)的角色
你用Test.Aux[A,B]作为类型别名来暴露细化后的类型,这本身是没问题的,但编译器在查找隐式转换时,需要明确匹配这个类型的转换规则。现在没有标记implicit的toAllOps,编译器根本不知道它可以把Test.Aux[A,B]转换成带有foo方法的Ops[A,B]。
快速修复方案
有两种常用的方式让你的扩展方法能被自动调用:
方案一:把toAllOps改成隐式转换方法
修改syntax对象,给toAllOps加上implicit关键字:
object syntax { // 关键:添加implicit标记,让编译器自动调用这个转换 implicit def toAllOps[A,B](t : Test.Aux[A,B])(implicit tc : MyTypeclass[A,B]) = new Ops[A,B] { val instance = tc val self = t } }
然后在使用时导入这个隐式转换:
import MyTypeclass.syntax._ Test[String,Int].foo(2.0) // 现在就能正常编译运行了
方案二:使用隐式类(Scala 2的惯用写法)
隐式类是Scala 2中实现扩展方法更简洁的方式,它会自动生成对应的隐式转换:
object MyTypeclass { trait Ops[A,B] { def self : Test.Aux[A,B] def instance : MyTypeclass[A,B] def foo[C](c : C) : Test.Aux[A,C] = instance.foo[C](self,c) } object syntax { // 隐式类自动生成隐式转换,把Test.Aux[A,B]包装成TestOps implicit class TestOps[A,B](val self: Test.Aux[A,B])(implicit tc: MyTypeclass[A,B]) extends Ops[A,B] { override val instance = tc } } }
同样导入MyTypeclass.syntax._后,就能直接调用foo方法了。
再回头看你的测试代码
倒数第二行toAllOps(Test[String,Int]).foo(2.0)能运行,是因为你手动把Test.Aux[String,Int]实例转换成了Ops[String,Int]——相当于你替编译器做了它本该自动做的事。而最后一行没有这个转换步骤,编译器找不到foo方法,自然就报错了。
内容的提问来源于stack exchange,提问作者Luciano

