簡體   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