scala – 类型成员的上下文边界或如何在成员实例化之前推迟隐式
在下面的示例中,有没有办法避免隐式解析选择defaultInstance并使用intInstance?代码后的更多背景:
// the following part is an external fixed API trait TypeCls[A] { def foo: String } object TypeCls { def foo[A](implicit x: TypeCls[A]) = x.foo implicit def defaultInstance[A]: TypeCls[A] = new TypeCls[A] { def foo = "default" } implicit val intInstance: TypeCls[Int] = new TypeCls[Int] { def foo = "integer" } } trait FooM { type A def foo: String = implicitly[TypeCls[A]].foo } // end of external fixed API class FooP[A:TypeCls] { // with type params,we can use context bound def foo: String = implicitly[TypeCls[A]].foo } class MyFooP extends FooP[Int] class MyFooM extends FooM { type A = Int } object Main extends App { println(s"With type parameter: ${(new MyFooP).foo}") println(s"With type member: ${(new MyFooM).foo}") } 实际产量: With type parameter: integer With type member: default 期望的输出: With type parameter: integer With type member: integer 我正在使用第三方库,该库使用上述方案为类型类TypeCls提供“默认”实例.我认为上面的代码是一个演示我的问题的最小例子. 用户应该混合FooM特性并实例化抽象类型成员A.问题是由于defaultInstance调用(new MyFooM).foo不解析专用的intInstance而是提交到defaultInstance,这不是我的意思想. 我添加了一个使用类型参数的替代版本,称为FooP(P = Parameter,M = Member),它避免通过使用type参数上的上下文来解析defaultInstance. 对于类型成员,是否有相同的方法来执行此操作? 编辑:我的简化中有一个错误,实际上foo不是def而是val,因此无法添加隐式参数.因此目前的答案都不适用. trait FooM { type A val foo: String = implicitly[TypeCls[A]].foo } // end of external fixed API class FooP[A:TypeCls] { // with type params,we can use context bound val foo: String = implicitly[TypeCls[A]].foo } 解决方法
在这个特定情况下最简单的解决方案是foo本身需要一个TypeCls [A]的隐式实例.
唯一的缺点是它会在每次调用foo时传递,而不是在实例化时传递 FooM.所以你必须确保它们在每次调用foo时都在范围内.虽然只要TypeCls实例在伴随对象中,您就没有任何特殊要做. trait FooM { type A def foo(implicit e: TypeCls[A]): String = e.foo } 更新:在上面的回答中,我设法错过了FooM无法修改的事实.此外,该问题的最新编辑提到FooM.foo实际上是一个val而不是def. 好消息是你正在使用的API被破坏了. FooM.foo wille无法返回任何有用的东西(无论A的实际值如何,它都会将TypeCls [A]解析为TypeCls.defaultInstance).唯一的出路是在已知A的实际值的派生类中覆盖foo,以便能够使用TypeCls的正确实例.幸运的是,这个想法可以与您使用具有上下文绑定的类(在您的情况下为FooP)的原始解决方法相结合: class FooMEx[T:TypeCls] extends FooM { type A = T override val foo: String = implicitly[TypeCls[A]].foo } 现在不要让你的类直接扩展FooM,而是让它们扩展FooMEx: class MyFoo extends FooMEx[Int] FooMEx与原始FooP类之间的唯一区别是FooMEx确实扩展了FooM,因此MyFoo是FooM的正确实例,因此可以与固定API一起使用. (编辑:李大同) 【声明】本站内容均来自网络,其相关言论仅代表作者个人观点,不代表本站立场。若无意侵犯到您的权利,请及时与联系站长删除相关内容! |