[英]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
)
這個值可以從Kkk
和Aaaa
。 但隱含的值應該在范圍內定義,你實際上在混合Aaaa
。
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.