如何覆盖一个导入的隐式值? [英] How to override an implicit value, that is imported?
问题描述
我已经尝试过解决方案,如如何覆盖隐式值中所述?,但无济于事.这是一个代码示例.
I have tried solutions, described in How to override an implicit value?, but it does not help. Here is a code example.
具有2种不同实现的TestImplicit抽象定义(类似ExecutionContextExecutor
):
A definition of TestImplicit abstraction with 2 different implementations (analogue of ExecutionContextExecutor
):
trait TestImplicit {
def f(s:String):Unit
}
object TestImplicitImpl1 extends TestImplicit {
override def f(s: String): Unit = println(s"1: $s")
}
object TestImplicitImpl2 extends TestImplicit {
override def f(s: String): Unit = println(s"2: $s")
}
在ImplDefinition
对象中,定义了q
变量以通过import(ExecutionContext.Implicits.global
的类似物)隐式使用:
And in the ImplDefinition
object a q
variable is defined to be used implicitly via import (analogue of ExecutionContext.Implicits.global
):
object ImplDefinition {
implicit val q:TestImplicit = TestImplicitImpl1
}
定义方法的客户端,隐式接受TestImplicit
(类似于scala.concurrent.Future):
Client that defines a method, accepting TestImplicit
implicitly (analogue of scala.concurrent.Future):
trait TestImplicitClient {
def fu(implicit ti:TestImplicit):Unit
}
object TestImplicitClient extends TestImplicitClient {
override def fu(implicit ti: TestImplicit): Unit = {
println("client")
ti.f("param")
}
}
下一步是选择要使用TestImplicit
的实现的客户端客户端,该决定通过import
(使用Future
的API的模拟)完成:
The next step, a client of client, that chooses which implementation of TestImplicit
should be used, the decision is done via import
(analogue of API that uses Future
):
object ClientOfClient {
import somepackage.ImplDefinition.q
def t():Unit =
TestImplicitClient.fu
}
现在在测试中,我想使用此ClientOfClient.t()
,但是我需要覆盖隐式,并改用TestImplicitImpl2
.背后的主要思想-隐式应该由API的客户端定义/覆盖,而不是由API本身定义:
Now in test, I want to use this ClientOfClient.t()
, but I need to override implicit, and use TestImplicitImpl2
instead. The main idea behind - implicits should be defined/overridable by the client of API, but not by API itself:
import somepackage.{ClientOfClient, TestImplicit, TestImplicitImpl2}
import org.junit.Test
class ImplTest {
// trying to hide it via import, does not help
import somepackage.ImplDefinition.{q => _,_}
@Test def test(): Unit ={
//trying to hide it via downgrading to non-implicit, does not work either
val q = somepackage.ImplDefinition.q
implicit val ti = TestImplicitImpl2
ClientOfClient.t()
}
}
每次运行测试时,我都会得到输出:
Each time I run test, I get in the output:
client
1: param
但没想到:
client
2: param
我该如何解决?我需要一种允许客户端重写隐式并使用尽可能简单的API的方法.这意味着,我不想在ClientOfClient.t()
方法中添加其他隐式参数.
How can I fix it? I need a way to allow clients to override implicits and stay with as simple API as possible. Which means, I do not want to add additional implicit parameter into ClientOfClient.t()
method.
推荐答案
在任何地方都有一个带有硬编码常量TestImplicitImpl1
的单例对象ClientOfClient
时,实际上您将无能为力.但是有几种解决方法.
As soon as you have a singleton object ClientOfClient
with a hard-coded constant TestImplicitImpl1
everywhere, there is essentially nothing you can do. But there are several work-arounds.
object ClientOfClient {
def t()(implicit ti: TestImplicit = ImplDefinition.q): Unit =
TestImplicitClient.fu
}
object ImplTest {
def test(): Unit = {
implicit val ti2 = TestImplicitImpl2
ClientOfClient.t()
}
}
ImplTest.test() // 2: param
2.通过可以覆盖的单独方法提供隐式
如果要使隐式重写可重写,则使ClientOfClient
可扩展,并创建一个返回隐式方法的方法(此处为"cocti
"),而不是直接按import
隐式操作.然后,您可以覆盖该方法(而您不能覆盖导入).
2. Supply the implicit through a separate method that can be overridden
If you want to make the implicit overridable, then make ClientOfClient
extendable, and create a method (here "cocti
") that returns the implicit, instead of import
ing the implicit directly. You can then override the method (whereas you cannot override an import).
这在一天结束时产生2: param
:
trait TestImplicit {
def f(s: String): Unit
}
object TestImplicitImpl1 extends TestImplicit {
override def f(s: String): Unit = println(s"1: $s")
}
object TestImplicitImpl2 extends TestImplicit {
override def f(s: String): Unit = println(s"2: $s")
}
object ImplDefinition {
implicit val q: TestImplicit = TestImplicitImpl1
}
trait TestImplicitClient {
def fu(implicit ti: TestImplicit): Unit
}
object TestImplicitClient extends TestImplicitClient {
override def fu(implicit ti: TestImplicit): Unit = {
println("client")
ti.f("param")
}
}
class ClientOfClient {
implicit def cocti: TestImplicit = {
ImplDefinition.q
}
def t():Unit =
TestImplicitClient.fu
}
object ImplTest {
def test(): Unit = {
implicit val ti2 = TestImplicitImpl2
new ClientOfClient {
override def cocti = ti2
}.t()
}
}
ImplTest.test() // 2: param
这篇关于如何覆盖一个导入的隐式值?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!