如何覆盖一个导入的隐式值? [英] How to override an implicit value, that is imported?

查看:88
本文介绍了如何覆盖一个导入的隐式值?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我已经尝试过解决方案,如如何覆盖隐式值中所述?,但无济于事.这是一个代码示例.

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 importing 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屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆