首页 > 解决方案 > 从 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.

非常感谢任何帮助。

标签: scalaplayframeworkspecs2

解决方案


  1. Scala 可以推断类型参数。由于传递的参数的类型都与类型参数无关,因此会限制要推断的类型,因此 scala 编译器只会将其推断为 type 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文档。


推荐阅读