本文介绍了Scala:为什么没有隐式找到隐式ExecutionContext?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我有一个特征:

trait Crawler {
  implicit def system: ActorSystem

  implicit def executionContext: ExecutionContext

  implicit def materializer: Materializer

  // other methods
}

和一个测试班:

class CrawlerSpec extends AsyncFlatSpec with Matchers with Crawler {
  override implicit val system: ActorSystem = ActorSystem("ufo-sightings")

  override implicit val executionContext: ExecutionContext = implicitly[ExecutionContext]

  override implicit val materializer: Materializer = ActorMaterializer()

  // test
}

根据Scalatest 文档:

According to Scalatest doc:

但是由于 ExecutionContext 为null(应该正常失败,但这是另一回事),该测试因NPE而破裂.

But the test blows up with a NPE due to the ExecutionContext being null (should fail gracefully, but that's another matter).

java.lang.NullPointerException was thrown.
java.lang.NullPointerException
    at scala.concurrent.impl.Future$.apply(Future.scala:31)
    at scala.concurrent.Future$.apply(Future.scala:494)

为什么没有选择隐式的 ExecutionContext ?

Why isn't the implicit ExecutionContext picked up?

<rant>
  Implicit resolution is a nightmare. At the expense of saving a few
  keystrokes, it makes code so fragile that removal of a single
  import breaks it. There's a reason other statically typed languages like
  Haskell or Kotlin don't have it; it's a stinking mess.
</rant>

推荐答案

让我们看看这里发生了什么:

Let's see what happens here:

trait A {
  implicit def executionContext: ExecutionContext
}

您在此处声明 A 将提供隐式值.然后

you declare here that A would provide implicit value. Then

class B extends A {
  override implicit val executionContext: ExecutionContext = implicitly[ExecutionContext]
}

那么这里会发生什么呢?

So what does happen here?

  1. executionContext B 构建期间初始化.
  2. 然后隐式尝试查找具有 ExecutionContext 类型的值.
  3. 找到这样的值: executionContext .
  1. value executionContext is initialized during B construction.
  2. then implicitly tries to find a value with ExecutionContext type.
  3. it finds such value: executionContext.

因此,您有效地执行了以下操作:

So effectively you did something like:

class B extends A {

  val executionContext: ExecutionContext = executionContext
}

您创建了对初始化的循环依赖关系:您正在用自身对其进行初始化.因此,您可以通过"getter"获得一个值,该值返回仍为 null 的属性(因为它刚刚被初始化).

You created circular dependency on initialization: you are initializing value with itself. So you take a value though a "getter" returning a property that is still null (as it is just being initialized).

我同意,隐式概念是需要付出很多努力的事情,尽管我不会像您那样反驳它们.在这里,您在循环依赖初始化方面遇到了问题.它不能正常失败,除了 Exception 之外的任何其他内容都会使程序进入无效状态.

I agree, that implicits are concepts are something that requires a lot of effort, though I would not antagonize them as much as you. Here you had a problem with circular dependency on initialization. It cannot fail gracefully, anything other than Exception would put the program into invalid state.

解决方案将初始化您的 implicit 值,而无需使用 implicitly .只需手动添加一些价值即可.或者不要使用该特征并从其他地方隐式导入.

Solution would be initializing you implicit value without the usage of implicitly. Just put some value there by hand. Or don't use that trait and import implicit from somewhere else.

这篇关于Scala:为什么没有隐式找到隐式ExecutionContext?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持!

05-22 07:20