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

如何将外部作用域中的值隐式注入Scala特性,scala,implicit,traits,self-type,Scala,Implicit,Traits,Self Type,我试图定义一个可重用的特性,它期望值在外部范围内。我可以在外部范围内定义trait,它可以工作,但不能重用。当我将trait移动到一个单独的作用域时,trait无法访问该值,而且我还没有找到一种方法将其声明为存在于trait所混合到的类型的外部作用域中 到目前为止,我得到的最接近的结果是: import javafx.beans.property.ObjectProperty import akka.actor.{Props, ActorSystem} import javafx.event

我试图定义一个可重用的特性,它期望值在外部范围内。我可以在外部范围内定义trait,它可以工作,但不能重用。当我将trait移动到一个单独的作用域时,trait无法访问该值,而且我还没有找到一种方法将其声明为存在于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()
    }
  }
}
它与我所追求的非常接近,唯一的缺点是客户端代码需要将外部范围中的值声明为隐式。理想情况下,我希望客户端代码在不改变任何其他内容的情况下混合到trait中

在本例中,我可以从“MicroServicesPrimaryStage”中使用“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
  ...
我认为cake模式本身无法解决这个问题,因为问题在于类型系统如何与外部范围中的定义交互

编辑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’在范围内,但不被认为是‘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”。这不是你想做的吗?

如果我也错过了什么,我也很抱歉

这就是经典的夏洛特蛋糕图案

或者,也许你想要一个水果蛋糕,在下一层再加上一个惊喜。(也许国王蛋糕是一个更好的比喻。)


我不知道为什么它应该是黄色的,除了在这种情况下黄色比磅更有趣。(最新消息:夏洛特在技术上更为正确;但从这个季节的精神来看,水果蛋糕可能是你想要的。)

也许这就是你想要的:

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
scala>抽象类Aaaa(隐式vala:Int)
定义类别Aaaa
scala>类Kkk扩展Aaaa
:9:错误:找不到参数a:Int的隐式值
类Kkk扩展Aaaa
^
scala>隐式val a=5
a:Int=5
scala>类Kkk扩展Aaaa
定义类Kkk
scala>新Kkk
res12:Kkk=Kkk@1a79ef3
scala>res12.a
res13:Int=5
让我们想象一下,
Int
是一个
ActorSystem


可从
Kkk
Aaaa
访问此值。但是隐式值应该在作用域中定义,在这个作用域中,您实际上混合了
Aaaa

不确定这是否有帮助,但是您可以声明该特征在何处可以声明的约束,例如:
trait Tax{this:Trade=>
(from:)感谢您的建议。我尝试了自类型,并选择直接扩展“微服务”,而不是限制最终类型。这可能是一个更好的起点,但我认为这两种方法都会导致编译器需要在最终类型(“微服务PrimaryStage”)中显式定义“actorSystem”。我认为有意思的是,外部对象的成员不被认为是在外部对象的作用域内混合在一起的类的成员。我发现这个答案解释了差异,也许自类型更清楚地表达了意图:自包含代码是有用的(或者build.sbt,因为我懒惰),并且更新问题以反映注释中的喋喋不休是有帮助的。这是另一种情况。我尝试使用外部范围中的值作为定义,而不重新定义它。如果您将“val foo”定义从“类栏”移到“对象FooBar”,您的示例将更接近我试图实现的目标-但这不会实现这就是为什么我必须使用隐式。啊,好吧,我明白了,你在说什么。好吧,如果你的问题只是使用隐式,那么它不一定是。为什么不简单地将
system
作为参数传递给类构造函数呢?我更新了我的答案来说明这一点。我实际上是按照你说的做的,但是(几乎)使用隐式对客户端代码隐藏参数的存在。Scala让我走到了这一步,这非常令人惊讶,但我仍然在寻找一种“更干净”的方法,从客户端代码的角度实现相同的结果。是的,确实是“隐藏”它…我只是不确定它是不是一件好事。你喜欢魔术吗?有趣的是,它仍然通过一个派生类工作,没有自己的隐式声明。隐式在类定义时应用,就像
类Kkk扩展Aaaa()(5)
我认为您已经了解了一些东西,但是将继承转移到外部对象会产生不同的问题。我开始研究这个问题,因为我发现ScalaFX窗口的默认关闭行为有点原始,我认为这很容易通过对客户端代码进行一行更改来解决,只需重新使用一点即可可以在后台使用“库”代码。隐式的方法还可以,但我认为一定有更好的方法。自类型是特征继承的替代方法,但在客户端代码的可用性方面,这两种方法是等效的。
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