如何覆盖一个隐式值,即导入?

时间:2018-07-30 12:07:40

标签: scala implicit overrides

我已经尝试了How to override an implicit value?中所述的解决方案,但无济于事。这是一个代码示例。

具有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变量,以通过import(与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(使用Future的API的模拟)来做出决定:< / p>

object ClientOfClient {

  import somepackage.ImplDefinition.q

  def t():Unit =
    TestImplicitClient.fu
}

现在在测试中,我想使用此ClientOfClient.t(),但是我需要覆盖隐式,而改用TestImplicitImpl2。背后的主要思想-隐式应该由API的客户端定义/重写,而不是由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()方法中添加其他隐式参数。

1 个答案:

答案 0 :(得分:1)

一旦到处都有一个带有硬编码常量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”),而不是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