如何覆盖导入的隐式值?
How to override an implicit value, that is imported?
我已经尝试过 中描述的解决方案,但没有帮助。这是一个代码示例。
具有 2 种不同实现的 TestImplicit 抽象的定义(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
变量以通过导入隐式使用(类似于 ExecutionContext.Implicits.global
):
object ImplDefinition {
implicit val q:TestImplicit = TestImplicitImpl1
}
定义方法的客户端,隐式接受TestImplicit
(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
完成的(API 的模拟,使用 Future
):
object ClientOfClient {
import somepackage.ImplDefinition.q
def t():Unit =
TestImplicitClient.fu
}
现在在测试中,我想使用这个 ClientOfClient.t()
,但我需要覆盖隐式,并改用 TestImplicitImpl2
。背后的主要思想 - 隐式应该由 API 的客户 defined/overridable,而不是 API 本身:
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()
}
}
每次我运行测试,我在输出中得到:
client
1: param
但没想到:
client
2: param
我该如何解决?我需要一种方法来允许客户覆盖隐式并尽可能保持简单 API 。这意味着,我不想在 ClientOfClient.t()
方法中添加额外的隐式参数。
一旦你有了一个单例对象ClientOfClient
,到处都有一个硬编码的常量TestImplicitImpl1
,你基本上就无能为力了。但是有几种解决方法。
1。使用默认隐式参数
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
”)returns 隐式,而不是 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
我已经尝试过
具有 2 种不同实现的 TestImplicit 抽象的定义(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
变量以通过导入隐式使用(类似于 ExecutionContext.Implicits.global
):
object ImplDefinition {
implicit val q:TestImplicit = TestImplicitImpl1
}
定义方法的客户端,隐式接受TestImplicit
(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
完成的(API 的模拟,使用 Future
):
object ClientOfClient {
import somepackage.ImplDefinition.q
def t():Unit =
TestImplicitClient.fu
}
现在在测试中,我想使用这个 ClientOfClient.t()
,但我需要覆盖隐式,并改用 TestImplicitImpl2
。背后的主要思想 - 隐式应该由 API 的客户 defined/overridable,而不是 API 本身:
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()
}
}
每次我运行测试,我在输出中得到:
client
1: param
但没想到:
client
2: param
我该如何解决?我需要一种方法来允许客户覆盖隐式并尽可能保持简单 API 。这意味着,我不想在 ClientOfClient.t()
方法中添加额外的隐式参数。
一旦你有了一个单例对象ClientOfClient
,到处都有一个硬编码的常量TestImplicitImpl1
,你基本上就无能为力了。但是有几种解决方法。
1。使用默认隐式参数
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
”)returns 隐式,而不是 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