Scala:为什么隐式找不到隐含的ExecutionContext?

时间:2017-03-12 00:39:24

标签: scala implicit-conversion implicit scalatest

我有一个特点:

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 doc

  

异步样式特征扩展了AsyncTestSuite,它提供了一个   隐式scala.concurrent.ExecutionContext,名为executionContext。

但由于ExecutionContext为空(由于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

为什么隐含的<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> 没有被选中?

    //Polling response from URL
    HttpWebRequest request = (HttpWebRequest)WebRequest.Create(URL);
    HttpWebResponse response = (HttpWebResponse)request.GetResponse();
    StreamReader resStream = new StreamReader(response.GetResponseStream());

    //Parsing response to separate out each donation
    JObject ParsedJson = JObject.Parse(resStream.ReadToEnd());
    var TransactionArray = ParsedJson["donations"].Children();

    //Array to store parsed results into usable format
    List<DonationsJSON> TransArray = new List<DonationsJSON> { };
    foreach (object Transaction in TransactionArray)
    {
        // clean up parsed string and remove Anonymous ID string
        string DonationString = Transaction.ToString().Replace("\r\n", "");
        DonationString = DonationString.Substring(DonationString.IndexOf(':') + 1);

        // Deserializing string into Donation class and adding to List
        DonationsJSON TX = JsonConvert.DeserializeObject<DonationsJSON>(DonationString);
        TransArray.Add(TX);
    }

1 个答案:

答案 0 :(得分:4)

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

trait A {
  implicit def executionContext: ExecutionContext
}

您在此声明A将提供隐含价值。然后

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

那么这里发生了什么?

  1. executionContextB构建期间初始化。
  2. 然后implicitly尝试查找ExecutionContext类型的值。
  3. 它找到了这样的值:executionContext
  4. 如此有效地你做了类似的事情:

    class B extends A {
    
      val executionContext: ExecutionContext = executionContext
    }
    

    您在初始化时创建了循环依赖:您正在使用自身初始化值。所以你通过“getter”获取一个值,返回一个仍然是null的属性(因为它刚刚被初始化)。

    我同意,暗示概念是需要付出很多努力的东西,尽管我不会像你那样对抗它们。这里有一个循环依赖初始化的问题。它不能优雅地失败,除Exception之外的任何东西都会使程序进入无效状态。

    解决方案是在不使用implicit的情况下初始化implicitly值。只需手工放一些价值。或者不要使用该特征并从其他地方隐式导入。