【问题标题】:Creating an anonymous class from an abstract class in Scala - desugaring code从 Scala 中的抽象类创建匿名类 - 脱糖代码
【发布时间】:2019-06-01 03:11:18
【问题描述】:

这里是 Scala 菜鸟。

我不理解以下代码,取自 Play 应用程序的集成测试 (Scala):

package workflows.admin

import play.api.test._

class SignInSpec extends PlaySpecification {

  "An activated user" should {
    "be able to sign in to the admin console" in new WithBrowser(webDriver = WebDriverFactory(FIREFOX)) {
      // some matchers here ...
      true
    }
  }
}

据我了解,该示例从抽象类WithBrowser 创建了一个新的匿名类并将其实例化。该实例将接收(命名的)构造函数参数webDriver

问题是我在查看WithBrowser 时不明白这里发生了什么:

abstract class WithBrowser[WEBDRIVER <: WebDriver](
    val webDriver: WebDriver = WebDriverFactory(Helpers.HTMLUNIT),
    val app: Application = GuiceApplicationBuilder().build(),
    val port: Int = Helpers.testServerPort) extends Around with Scope {

  def this(
    webDriver: Class[WEBDRIVER],
    app: Application,
    port: Int) = this(WebDriverFactory(webDriver), app, port)

  implicit def implicitApp: Application = app
  implicit def implicitPort: Port = port

  lazy val browser: TestBrowser = TestBrowser(webDriver, Some("http://localhost:" + port))

  override def around[T: AsResult](t: => T): Result = {
    try {
      Helpers.running(TestServer(port, app))(AsResult.effectively(t))
    } finally {
      browser.quit()
    }
  }
}

我有两个问题:

  1. WithBrowser 是一个泛型抽象类,带有一个类型参数WEBDRIVER,但是示例中没有声明类型参数。相反,匿名类的实例使用命名构造函数参数webDriver 接收此信息。类型参数和构造函数参数之间缺少什么联系?
  2. 该示例声明了一个代码块(它只返回true),这个代码块就是测试本身。但是该代码在匿名类中的什么位置? WithBrowser 扩展了Around 并覆盖了执行代码块的around 函数,但我不明白生成的匿名类如何将给定的示例代码块移动到around

非常感谢任何帮助。

【问题讨论】:

    标签: scala playframework specs2


    【解决方案1】:
    1. Scala 可以推断类型参数。由于传递的参数的类型都与类型参数无关,因此会限制要推断的类型,scala 编译器只会将其推断为类型Nothing

    2. 代码块通常是类的构造函数,但这是一种特殊情况。 Around 类确实扩展了 scala.DelayedInit 接口。这会将构造函数中的代码重写为调用delayedInit 函数。实际上将是构造函数的代码作为按名称调用的参数传递给此函数。这个值(实际上包装在org.specs2.execute.Result.resultOrSuccess 调用中)是传递给around 函数的参数。

    想象一下Around 类是这样的:

    class Around extends DelayedInit {
      def delayedInit(f: => Unit): Unit = around(f)
      def around(f: => Unit): Unit
    }
    

    假设您现在将继承 around 类:

    class Foo extends Around {
      println("Hello World")
    }
    

    以上内容被改写为:

    class Foo extends Around {
      delayedInit(println("Hello World"))
    }
    

    如果我的解释不够清楚或者你想了解更多的实现细节:
    - 这是AroundDelayedInit 文档。

    【讨论】:

    • 这是“推断”,而不是“干涉”。
    • 谢谢阿基和布赖恩。这就解释了。我不知道 DelayedInit,我对这个特征的存在感到非常惊讶。根据用户命名空间中特征的存在来更改类初始化似乎是一个非常糟糕的黑客攻击。
    • DelayedInit 主要用于实现App 类。这个功能实际上有beendeprecated,因为它在发布2.11.0时表现出令人惊讶的行为。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2023-03-17
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多