繁体   English   中英

如何将外部作用域中的值隐式注入Scala特征

[英]How to implicitly inject a value from an outer scope into a Scala trait

我正在尝试定义一个可重用的特性,它期望值在外部范围内。 我可以在外部范围内定义特征,它可以工作,但不能重复使用。 当我将特征移动到一个单独的范围时,特征无法访问该值,并且我还没有找到一种方法将其声明为特征混合所属类型的外部范围。

我到目前为止最接近的是:

import javafx.beans.property.ObjectProperty

import akka.actor.{Props, ActorSystem}

import javafx.event.EventHandler
import javafx.stage.{WindowEvent => JWindowEvent}

import scalafx.application.{Platform, JFXApp}
import scalafx.scene.Scene
import scalafx.scene.canvas.Canvas
import scalafx.scene.paint.Color


object MicroServicesApp extends JFXApp {
  implicit val system = ActorSystem("system")

  val canvas = new Canvas {
    width = 1200
    height = 900
  }

  stage = new MicroServicesPrimaryStage with AutomaticMicroServicesWindowCloser {
    title.value = "Map Viewer"

    scene = new Scene {
      fill = Color.LightGreen

      content = canvas
    }
  }
}

class MicroServicesPrimaryStage(implicit val actorSystem: ActorSystem) extends JFXApp.PrimaryStage with MicroServices {
}

/**
 * A class enabled with a micro-services actor system.
 */
trait MicroServices {
  def actorSystem: ActorSystem
}

/**
 * An automatic window closer for a ScalaFX and Akka micro-services application.
 *
 * When this trait is mixed in to a class with the MicroServices trait and the onCloseRequest property,
 * the onCloseRequest property will be initialized with a useful default event handler that shuts down
 * the Akka actor system as well as the ScalaFX platform.
 */
trait AutomaticMicroServicesWindowCloser extends MicroServicesWindowCloser {
  def onCloseRequest: ObjectProperty[EventHandler[JWindowEvent]]

  def onCloseRequest_=(handler: EventHandler[JWindowEvent]): Unit

  onCloseRequest = closeRequest()
}

/**
 * A window closer for a ScalaFX and Akka micro-services application.
 */
trait MicroServicesWindowCloser extends MicroServices {
  def closeRequest(): EventHandler[JWindowEvent] = new EventHandler[JWindowEvent] {
    override def handle(e: JWindowEvent)
    {
      println("... closing application.")

      actorSystem.shutdown()
      Platform.exit()
    }
  }
}

它非常接近我所追求的,唯一的赠与是客户端代码需要声明外部作用域中的值是隐式的。 理想情况下,我希望客户端代码混合使用特征而不更改任何其他内容。

在示例中,我可以在'MicroServicesPrimaryStage'中使用'system',但不能在混合特性中使用'system'。 我认为这是因为'system'在范围内,但不被认为是'MicroServicesPrimaryStage'的成员。

我可以使用val或def为'system'创建一个别名,并使其以这种方式工作,但这也意味着修改客户端代码的额外步骤。 如果特征可能需要“系统”的定义并且能够在特征混合的点处的​​外部范围中找到它,那将是很好的。

这可能吗?

编辑1

这两个println语句说明了我混淆的原因:

stage = new MicroServicesPrimaryStage with AutomaticMicroServicesWindowCloser {
  println(s"val system is accessible from outer scope: $system ...")                        // compiles
  println(s"... but is not mixed-in to MicroServicesPrimaryStage as ${this.system}.")     // does not compile
  ...

我不认为蛋糕模式可以单独解决这个问题,因为问题是关于类型系统如何与外部范围中的定义交互。

编辑2

用于Java 8的SBT文件:

name := "workspace-sbt"

version := "1.0"

scalaVersion := "2.11.4"

resolvers += Opts.resolver.sonatypeSnapshots

libraryDependencies ++= Seq("org.scalatest"     %  "scalatest_2.11" % "2.2.1" % "test",
                            "org.scalafx"       %% "scalafx"        % "8.0.20-R7-SNAPSHOT",
                            "com.typesafe.akka" %% "akka-actor"     % "2.3.7")

你误会了:

“在示例中,我可以使用'MicroServicesPrimaryStage'中的'system',但不能使用混合特性中的'system'。我认为这是因为'system'在范围内,但不被认为是'MicroServicesPrimaryStage'的成员“。

这不是真的。 您当然可以使用超类成员作为混合特征的抽象成员的定义。 考虑一下:

trait Foo { 
    def foo: String 
    def printFoo = println(foo)
}

class FooBar(val foo)

object FooBar {
    def main(argv: Array[String]) = new FooBar("foo") with Foo printFoo
}

如果运行,这将编译并打印“foo”。 这不是你想要做的吗?

对不起,如果我也错过了什么。

这只是经典的夏洛特蛋糕模式。

或者,也许你要的是一个水果蛋糕,下一层会有额外的惊喜。 (也许国王的蛋糕是一个更好的比喻。)

package cakesample

// something useful
trait Something {
  def thing: String
}

// a trait requiring something
trait Needy { _: Something =>
  def theThingIs: String = thing
}

// another trait that uses something
trait User { _: Something =>
  def use: String = thing * 2
}

// fruit cake fixings
case class Widget(w: String)

trait WidgetFramework {
  // used by the framework
  def widget: Widget

  trait WidgetCog {
    def run() = Console println s"Running ${widget.w}"
  }
}

// sample usage
object Test extends App with Something with Needy with User with WidgetFramework {
  // normal cake, a charlotte
  def thing = "hello, world"
  Console println s"$theThingIs: $use"

  // a fruit cake

  // define a widget
  val widget = Widget("my widget")

  // to be used by an object implementing a trait
  object client extends WidgetCog

  client.run()
}

我不知道为什么它应该是黄色的,除了在这种情况下黄色比英镑更有趣。 (更新:夏洛特在技术上更正确;但在本季的精神中,水果蛋糕可能就是你所追求的。)

也许这就是你要找的东西:

scala> abstract class Aaaa(implicit val a: Int)
defined class Aaaa

scala> class Kkk extends Aaaa
<console>:9: error: could not find implicit value for parameter a: Int
       class Kkk extends Aaaa
                         ^

scala> implicit val a = 5
a: Int = 5

scala> class Kkk extends Aaaa
defined class Kkk

scala> new Kkk
res12: Kkk = Kkk@1a79ef3

scala> res12.a
res13: Int = 5

让我们想象一下, Int是一个ActorSystem

这个值可以从KkkAaaa 但隐含的值应该在范围内定义,你实际上在混合Aaaa

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM