7

我对 Scala 还很陌生,一直在尝试学习和理解隐式转换和参数,并且遇到了一个让我感到困惑的场景。

对于上下文,我正在使用 Scaldi 在 Akka 应用程序中进行依赖注入,并希望从抽象类继承多个可注入的 Actor。我相信我无法准确地使抽象类成为特征,因为我们需要Injector通过构造函数参数使隐式可用以利用框架。

展示我所看到的行为的一个非常人为的示例如下:

class SecretSauce {}

abstract class Base(implicit secretSauce: SecretSauce) {}

class Concrete extends Base {}

object Example extends App {
    ... // Setup Actor system, etc, etc
    implicit val secretSauce: SecretSauce = new SecretSauce()
}

我原以为一切正常,但我得到了一个编译错误:

Unspecified value parameter secretSauce.
class Concrete extends Base {
             ^

如果我将隐式参数添加到具体类中,就像这样,事情会起作用:

class Concrete(implicit secretSauce: SecretSauce) extends Base {}

我认为我的困惑源于隐式参数的工作方式——在我所描述的情况下,它们不是由子类继承的吗?有人可以 ELI5 在我的示例中发生什么或指出可以帮助解决问题的参考吗?

谢谢!

4

2 回答 2

4

确定 Scala 编译器在何处查找隐式的确切规则有点复杂,但在大多数情况下,您只需要考虑隐式值可能来自两个地方:

  1. 当前范围。
  2. 所涉及的任何类型的伴随对象。

这意味着这将编译:

class SecretSauce {}

object SecretSauce {
  implicit val secretSauce: SecretSauce = new SecretSauce()
}

abstract class Base(implicit secretSauce: SecretSauce) {}

object Example extends App {
  class Concrete extends Base {}
}

或这个:

class SecretSauce {}

abstract class Base(implicit secretSauce: SecretSauce) {}

object Example extends App {
  implicit val secretSauce: SecretSauce = new SecretSauce()

  class Concrete extends Base {}
}

但是,在您的版本中,当编译器到达这一行时:

class Concrete extends Base {}

它将知道它需要找到一个隐式SecretSauce值,并且它将首先查看该行SecretSauce范围内的隐式值,然后再查看伴随对象(如果存在)中的隐式值。它也没有找到,所以它拒绝编译你的代码。

于 2015-12-07T21:53:53.727 回答
1

隐式参数从以下位置“解析”:

  • 当前范围内定义的隐式
  • 显式导入
  • 通配符导入

据我了解,为了定义class Concrete,需要定义或导入隐式。

我在这个 SO 答案中找到了一个很好的解释。

于 2015-12-07T21:50:05.573 回答