Scala的隐藏功能


149

每个Scala开发人员都应注意的Scala的隐藏功能是什么?

请为每个答案提供一项隐藏功能。


6
呵呵,这个问题与问题本身一样有用,因为它链接到其他隐藏的功能。干杯!
JohnMetta

1
@mettadore只需查看 右侧的相关链接。
Daniel C. Sobral

2
@JohnMetta:或使用标签

Answers:


85

好的,我必须再添加一个。RegexScala中的每个对象都有一个提取器(请参见上面的oxbox_lakes的答案),可让您访问匹配组。因此,您可以执行以下操作:

// Regex to split a date in the format Y/M/D.
val regex = "(\\d+)/(\\d+)/(\\d+)".r
val regex(year, month, day) = "2010/1/13"

如果您不习惯使用模式匹配和提取器,第二行看起来会令人困惑。无论何时定义valvar,关键字后面的内容不仅是标识符,而且是模式。这就是为什么这样工作:

val (a, b, c) = (1, 3.14159, "Hello, world")

右边的表达式创建一个Tuple3[Int, Double, String]可以匹配模式的(a, b, c)

在大多数情况下,您的模式都使用属于单例对象成员的提取器。例如,如果您编写类似

Some(value)

那么您将隐式调用提取器Some.unapply

但是您也可以在模式中使用类实例,而这就是这里发生的情况。val regex是的实例Regex,当您在模式中使用val 时,会隐式调用regex.unapplySequnapplyvs unapplySeq超出了此答案的范围),它将匹配组提取到中,将其匹配Seq[String]元素分配给变量年,月和日。


1
感谢张贴此!仅供参考,在第一版的第503页和第二版的第611页的“在Scala中编程”一书的“使用正则表达式进行提取”一章中提到了这一点。
保罗

51

结构类型定义-即通过其支持的方法描述的类型。例如:

object Closer {
    def using(closeable: { def close(): Unit }, f: => Unit) {
      try { 
        f
      } finally { closeable.close }
    }
}

请注意,除了具有方法之外,未定义参数的类型closeableclose


1
“ Scala编程”中甚至没有提到结构类型。尽管它们使用反射来调用正确的方法,但它们比其他类型传递技术要慢一些。(希望他们能提出一种加快速度的方法。)
Ken Bloom

1
并且也可以为它们创建别名,就像使用外部分配的接口一样(非常慢):type Closeable = {def close():Unit}
Alexey 2010年

45

类型构造器多态(又名高类型)

例如,如果没有此功能,则可以表达在一个列表上映射一个函数以返回另一个列表,或在一个树上映射一个函数以返回另一个树的想法。但你不能表达这种想法通常不高于种。

使用更高的种类,您可以捕获使用其他类型参数化的任何类型的想法。带有一个参数的类型构造函数被称为同类(*->*)。例如,List。返回另一个类型构造器的类型构造器被称为实物(*->*->*)。例如,Function1。但是在Scala中,我们有更多的种类,因此我们可以使用其他类型构造函数对类型构造函数进行参数化。所以他们很像((*->*)->*)

例如:

trait Functor[F[_]] {
  def fmap[A, B](f: A => B, fa: F[A]): F[B]
}

现在,如果您有Functor[List],则可以映射列表。如果有Functor[Tree],则可以在树上进行映射。但更重要的是,如果您拥有Functor[A] A的任何一种(*->*),都可以将函数映射到A


39

提取器,可让您将杂乱的if-elseif-else样式代码替换为模式。我知道这些并没有完全隐藏,但是我已经使用Scala几个月了,但并没有真正了解它们的功能。对于(很长的)示例,我可以替换为:

val code: String = ...
val ps: ProductService = ...
var p: Product = null
if (code.endsWith("=")) {
  p = ps.findCash(code.substring(0, 3)) //e.g. USD=, GBP= etc
}
else if (code.endsWith(".FWD")) {
  //e.g. GBP20090625.FWD
  p = ps.findForward(code.substring(0,3), code.substring(3, 9))
}
else {
  p = ps.lookupProductByRic(code)
}

有了这个,我认为这清楚得多

implicit val ps: ProductService = ...
val p = code match {
  case SyntheticCodes.Cash(c) => c
  case SyntheticCodes.Forward(f) => f
  case _ => ps.lookupProductByRic(code)
}

我必须在后台做一些腿部工作...

object SyntheticCodes {
  // Synthetic Code for a CashProduct
  object Cash extends (CashProduct => String) {
    def apply(p: CashProduct) = p.currency.name + "="

    //EXTRACTOR
    def unapply(s: String)(implicit ps: ProductService): Option[CashProduct] = {
      if (s.endsWith("=") 
        Some(ps.findCash(s.substring(0,3))) 
      else None
    }
  }
  //Synthetic Code for a ForwardProduct
  object Forward extends (ForwardProduct => String) {
    def apply(p: ForwardProduct) = p.currency.name + p.date.toString + ".FWD"

    //EXTRACTOR
    def unapply(s: String)(implicit ps: ProductService): Option[ForwardProduct] = {
      if (s.endsWith(".FWD") 
        Some(ps.findForward(s.substring(0,3), s.substring(3, 9)) 
      else None
    }
  }

但是,将其将业务逻辑分离到一个明智的地方这一事实值得这样做。我可以实现Product.getCode以下方法。

class CashProduct {
  def getCode = SyntheticCodes.Cash(this)
}

class ForwardProduct {
  def getCode = SyntheticCodes.Forward(this)     
}

这不像是开关吗?也许可以将其重构。
地理

14
模式就像涡轮增压开关:功能更强大,更清晰
oxbow_lakes 2009年

1
很好,但我不喜欢您必须使用隐式,因为它的范围比匹配{}还远。您还可以向ProductService添加一个方法,该方法通过代码查找Product。无论如何,您都将重构的代码段包装为一种方法,以便能够在任何地方使用它。
Martin Konicek 2010年


35

在scala 2.8中,可以使用包scala.util.control.TailCalls(实际上是在蹦床)来使用尾递归方法。

一个例子:

def u(n:Int):TailRec[Int] = {
  if (n==0) done(1)
  else tailcall(v(n/2))
}
def v(n:Int):TailRec[Int] = {
  if (n==0) done(5)
  else tailcall(u(n-1))
}
val l=for(n<-0 to 5) yield (n,u(n).result,v(n).result)
println(l)

35

案例类会自动混入Product特质,从而提供对字段的无类型索引访问,而不会产生任何反射:

case class Person(name: String, age: Int)

val p = Person("Aaron", 28)
val name = p.productElement(0) // name = "Aaron": Any
val age = p.productElement(1) // age = 28: Any
val fields = p.productIterator.toList // fields = List[Any]("Aaron", 28)

此功能还提供了一种简化方法来更改toString方法的输出:

case class Person(name: String, age: Int) {
   override def productPrefix = "person: "
}

// prints "person: (Aaron,28)" instead of "Person(Aaron, 28)"
println(Person("Aaron", 28)) 

32

它不是完全隐藏的,但肯定是一个广告不足的功能:scalac -Xprint

为了说明用途,请考虑以下来源:

class A { "xx".r }

使用scalac -Xprint:typer输出进行编译:

package <empty> {
  class A extends java.lang.Object with ScalaObject {
    def this(): A = {
      A.super.this();
      ()
    };
    scala.this.Predef.augmentString("xx").r
  }
}

请注意scala.this.Predef.augmentString("xx").r,这是implicit def augmentStringPredef.scala中目前的应用程序。

scalac -Xprint:<phase>将在某个编译器阶段之后打印语法树。要查看可用阶段,请使用scalac -Xshow-phases

这是了解幕后情况的好方法。

试试看

case class X(a:Int,b:String)

使用typer阶段可以真正感受到它的用处。


30

您可以定义自己的控制结构。它实际上只是功能和对象以及一些语法糖,但它们的外观和行为类似于真实的事物。

例如,以下代码定义dont {...} unless (cond)dont {...} until (cond)

def dont(code: => Unit) = new DontCommand(code)

class DontCommand(code: => Unit) {
  def unless(condition: => Boolean) =
    if (condition) code

  def until(condition: => Boolean) = {
    while (!condition) {}
    code
  }
}

现在,您可以执行以下操作:

/* This will only get executed if the condition is true */
dont {
  println("Yep, 2 really is greater than 1.")
} unless (2 > 1) 

/* Just a helper function */
var number = 0;
def nextNumber() = {
  number += 1
  println(number)
  number
}

/* This will not be printed until the condition is met. */
dont {
  println("Done counting to 5!")
} until (nextNumber() == 5) 


我很好奇,如果有人知道一种定义if-then-else块的方法,它带有可选的else标准类型的类型检查对象。
菲利普

@Philippe: zif[A : Zero](cond: => Boolean)(t: => A): A = if(cond) t else mzero。需要斯卡拉兹。
missingfaktor 2011年

26

@switch Scala 2.8中的注释:

应用于匹配表达式的注释。如果存在,则编译器将验证匹配是否已编译到tableswitch或lookupswitch,并且如果将其编译为一系列条件表达式,则发出错误。

例:

scala> val n = 3
n: Int = 3

scala> import annotation.switch
import annotation.switch

scala> val s = (n: @switch) match {
     |   case 3 => "Three"
     |   case _ => "NoThree"
     | }
<console>:6: error: could not emit switch for @switch annotated match
       val s = (n: @switch) match {

26

邓诺(Dunno),如果这确实是隐藏的,但我觉得很好。

带有2个类型参数的类型构造函数可以用中缀表示法编写

object Main {                                                                   
  class FooBar[A, B]

  def main(args: Array[String]): Unit = {
    var x: FooBar[Int, BigInt] = null
    var y: Int FooBar BigInt   = null
  }
}

1
真好!我可以想象有时候对提高可读性很有用。例如var foo2barConverter: Foo ConvertTo Bar,将使类型参数的顺序显而易见。
Esko Luontola'3

4
有时我会在某种程度上使用PartialFunction的代码中执行此操作:类型〜> [A,B] = PartialFunction [A,B]
raichoo 2011年

24

Scala 2.8引入了默认和命名参数,这使得可以添加新的“复制”方法,Scala将其添加到案例类中。如果您定义此:

case class Foo(a: Int, b: Int, c: Int, ... z:Int)

并且您想要创建一个与现有Foo相似的新Foo,但仅具有不同的“ n”值,那么您可以说:

foo.copy(n = 3)

3
警告:如果您从另一个继承一个case类,将不会覆盖copy方法。因此,您必须手动覆盖它
Alexey 2010年

相关:更
干净的

5
不再允许case类(Scala 2.8)从case类继承。谢谢Scala勋爵弃用了这个邪恶的继承。
olle kullberg 2010年

24

在scala 2.8中,您可以将@specialized添加到您的通用类/方法中。这将为原始类型创建类的特殊版本(扩展AnyVal),并节省不必要的装箱/拆箱成本: class Foo[@specialized T]...

您可以选择AnyVals的子集: class Foo[@specialized(Int,Boolean) T]...


1
您是否可以指出我更长的解释?我想了解更多。
帕维尔Prażak

23

扩展语言。我一直想在Java中做这样的事情(不能)。但是在Scala中,我可以拥有:

  def timed[T](thunk: => T) = {
    val t1 = System.nanoTime
    val ret = thunk
    val time = System.nanoTime - t1
    println("Executed in: " + time/1000000.0 + " millisec")
    ret
  }

然后写:

val numbers = List(12, 42, 3, 11, 6, 3, 77, 44)
val sorted = timed {   // "timed" is a new "keyword"!
  numbers.sortWith(_<_)
}
println(sorted)

并得到

Executed in: 6.410311 millisec
List(3, 3, 6, 11, 12, 42, 44, 77)

23

您可以为函数指定一个按名称调用参数(编辑:此参数不同于延迟参数!),直到该函数使用该参数时才对其求值(编辑:实际上,每次使用该函数时都会对其进行重新评估。用过的)。见这个常见问题的详细信息

class Bar(i:Int) {
    println("constructing bar " + i)
    override def toString():String = {
        "bar with value: " + i
    }
}

// NOTE the => in the method declaration.  It indicates a lazy paramter
def foo(x: => Bar) = {
    println("foo called")
    println("bar: " + x)
}


foo(new Bar(22))

/*
prints the following:
foo called
constructing bar 22
bar with value: 22
*/

我以为“ x:=> Bar”意味着x是一个不带参数并返回Bar的函数。因此,“ new bar(22)”只是一个匿名函数,并且像其他任何函数一样被评估为函数。
亚历克斯·布莱克

1
“ x:()=> Bar”定义不带参数并返回Bar的xa函数。x:=> Bar将x定义为按名称调用。请查看scala.sygneca.com/faqs/…,以了解更多详细信息
Agilefall

3
您显示的是按名字呼叫参数。尚未实现惰性参数:lampsvn.epfl.ch/trac/scala/ticket/240
ArtemGr 2010年

我认为,如果您像lazy val xx: Bar = x在方法中那样做某事,并且从那一刻起您才使用它,则可以将其用作惰性参数xx
Cristian Vrabie '02

20

您可以locally用来引入局部块而不会引起分号推断问题。

用法:

scala> case class Dog(name: String) {
     |   def bark() {
     |     println("Bow Vow")
     |   }
     | }
defined class Dog

scala> val d = Dog("Barnie")
d: Dog = Dog(Barnie)

scala> locally {
     |   import d._
     |   bark()
     |   bark()
     | }
Bow Vow
Bow Vow

locally 在“ Predef.scala”中定义为:

@inline def locally[T](x: T): T = x

内联,不增加任何额外的开销。



17

早期初始化:

trait AbstractT2 {
  println("In AbstractT2:")
  val value: Int
  val inverse = 1.0/value
  println("AbstractT2: value = "+value+", inverse = "+inverse)
}

val c2c = new {
  // Only initializations are allowed in pre-init. blocks.
  // println("In c2c:")
  val value = 10
} with AbstractT2

println("c2c.value = "+c2c.value+", inverse = "+c2c.inverse)

输出:

In AbstractT2:  
AbstractT2: value = 10, inverse = 0.1  
c2c.value = 10, inverse = 0.1

我们实例化一个匿名内部类,valuewith AbstractT2子句之前初始化块中的字段。如运行脚本时所示,这可以确保value在执行主体之前对其进行初始化AbstractT2


1
该构造称为“早期初始化”。
兰德尔·舒尔茨

17

您可以使用“ with”关键字组成结构类型

object Main {
  type A = {def foo: Unit}
  type B = {def bar: Unit}

  type C = A with B

  class myA {
    def foo: Unit = println("myA.foo")
  }


  class myB {
    def bar: Unit = println("myB.bar")
  }
  class myC extends myB {
    def foo: Unit = println("myC.foo")
  }

  def main(args: Array[String]): Unit = { 
    val a: A = new myA 
    a.foo
    val b: C = new myC 
    b.bar
    b.foo
  }
}

17

匿名函数的占位符语法

从Scala语言规范中:

SimpleExpr1 ::= '_'

一个表达式(属于语法类别Expr)可以_在标识符合法的位置包含嵌入的下划线符号。这样的表达式表示一个匿名函数,其后出现的下划线表示连续的参数。

Scala语言更改

_ + 1                  x => x + 1
_ * _                  (x1, x2) => x1 * x2
(_: Int) * 2           (x: Int) => x * 2
if (_) x else y        z => if (z) x else y
_.map(f)               x => x.map(f)
_.map(_ + 1)           x => x.map(y => y + 1)

使用此方法,您可以执行以下操作:

def filesEnding(query: String) =
  filesMatching(_.endsWith(query))

2
这应称为“匿名函数的占位符语法”。隐式在Scala中具有独特的含义,并且与此无关。
Retronym 2010年

该链接与答案之间没有明显的关系。“隐式”不是正确的术语。如上所述,它应该是“占位符”。
阿兰·奥迪

2
它并不是真正的“隐藏”,在我阅读过的几乎所有有关Scala的教程中,我都已经看到了这种用法... :-)但是,我感谢我还没有看到的正式定义。
PhiLho 2011年

@PhiLho可能在2009年不太知名。我不知道。
尤金·横田

我错过了原始日期,因为只显示了最后一个编辑日期。而且,并不是该线程中解释的所有功能都是“隐藏的”。反正很酷的线程和好的答案。
PhiLho 2011年

16

隐式定义,特别是转换。

例如,假设有一个函数将输入字符串的中间部分替换为“ ...”,从而格式化输入字符串以适合其大小:

def sizeBoundedString(s: String, n: Int): String = {
  if (n < 5 && n < s.length) throw new IllegalArgumentException
  if (s.length > n) {
    val trailLength = ((n - 3) / 2) min 3
    val headLength = n - 3 - trailLength
    s.substring(0, headLength)+"..."+s.substring(s.length - trailLength, s.length)
  } else s
}

您可以将其与任何String一起使用,并且当然可以使用toString方法转换任何内容。但是您也可以这样写:

def sizeBoundedString[T](s: T, n: Int)(implicit toStr: T => String): String = {
  if (n < 5 && n < s.length) throw new IllegalArgumentException
  if (s.length > n) {
    val trailLength = ((n - 3) / 2) min 3
    val headLength = n - 3 - trailLength
    s.substring(0, headLength)+"..."+s.substring(s.length - trailLength, s.length)
  } else s
}

然后,您可以通过执行以下操作来传递其他类型的类:

implicit def double2String(d: Double) = d.toString

现在,您可以调用该函数并传递一个double值:

sizeBoundedString(12345.12345D, 8)

最后一个参数是隐式的,并且由于隐式de声明而被自动传递。此外,在sizeBoundedString内部 “ s” 当作 String对待,因为存在从它到String的隐式转换。

更好地为不常见的类型定义此类型的隐式,以避免意外的转换。您还可以显式传递一个转换,它仍将在sizeBoundedString内部隐式使用:

sizeBoundedString(1234567890L, 8)((l : Long) => l.toString)

您也可以具有多个隐式参数,但是您必须要么全部传递它们,要么不传递任何一个。还有一个用于隐式转换的快捷语法:

def sizeBoundedString[T <% String](s: T, n: Int): String = {
  if (n < 5 && n < s.length) throw new IllegalArgumentException
  if (s.length > n) {
    val trailLength = ((n - 3) / 2) min 3
    val headLength = n - 3 - trailLength
    s.substring(0, headLength)+"..."+s.substring(s.length - trailLength, s.length)
  } else s
}

使用方法完全相同。

隐式可以有任何价值。例如,它们可以用于隐藏库信息。以以下示例为例:

case class Daemon(name: String) {
  def log(msg: String) = println(name+": "+msg)
}

object DefaultDaemon extends Daemon("Default")

trait Logger {
  private var logd: Option[Daemon] = None
  implicit def daemon: Daemon = logd getOrElse DefaultDaemon

  def logTo(daemon: Daemon) = 
    if (logd == None) logd = Some(daemon) 
    else throw new IllegalArgumentException

  def log(msg: String)(implicit daemon: Daemon) = daemon.log(msg)
}

class X extends Logger {
  logTo(Daemon("X Daemon"))

  def f = {
    log("f called")
    println("Stuff")
  }

  def g = {
    log("g called")(DefaultDaemon)
  }
}

class Y extends Logger {
  def f = {
    log("f called")
    println("Stuff")
  }
}

在此示例中,在Y对象中调用“ f”会将日志发送到默认守护程序,在X的实例上将日志发送到Daemon X守护程序。但是,在X的实例上调用g会将日志发送到显式给定的DefaultDaemon。

尽管可以使用重载和私有状态来重写此简单示例,但是隐式不需要私有状态,并且可以通过导入将其带入上下文。


13

也许不太隐藏,但是我认为这很有用:

@scala.reflect.BeanProperty
var firstName:String = _

这将自动为与bean约定匹配的字段生成一个getter和setter方法。

developerWorks上的进一步描述


6
如果您经常使用它,则可以为其创建快捷方式,例如:import scala.reflect。{BeanProperty => BP}
Alexey 2010年

13

闭包中的隐式参数。

与方法一样,可以将函数参数标记为隐式。在函数主体的范围内,隐式参数是可见的,并且可以进行隐式解析:

trait Foo { def bar }

trait Base {
  def callBar(implicit foo: Foo) = foo.bar
}

object Test extends Base {
  val f: Foo => Unit = { implicit foo =>
    callBar
  }
  def test = f(new Foo {
    def bar = println("Hello")
  })
}


12

结果类型取决于隐式分辨率。这可以给您多种分派的形式:

scala> trait PerformFunc[A,B] { def perform(a : A) : B }
defined trait PerformFunc

scala> implicit val stringToInt = new PerformFunc[String,Int] {
  def perform(a : String)  = 5
}
stringToInt: java.lang.Object with PerformFunc[String,Int] = $anon$1@13ccf137

scala> implicit val intToDouble = new PerformFunc[Int,Double] {
  def perform(a : Int) = 1.0
}
intToDouble: java.lang.Object with PerformFunc[Int,Double] = $anon$1@74e551a4

scala> def foo[A, B](x : A)(implicit z : PerformFunc[A,B]) : B = z.perform(x)
foo: [A,B](x: A)(implicit z: PerformFunc[A,B])B

scala> foo("HAI")
res16: Int = 5

scala> foo(1)
res17: Double = 1.0

可能是这样,但是上面的会话具有误导性。在执行这些命令之前,必须已在环境中存在foo使用的定义a。我想你是故意的z.perform(x)
Daniel C. Sobral

4

Scala等效于Java双括号初始化程序。

Scala允许您使用该类的主体(构造函数)创建一个匿名子类,该主体包含用于初始化该类实例的语句。

当构建基于组件的用户界面(例如Swing和Vaadin)时,此模式非常有用,因为它允许创建UI组件并更简洁地声明其属性。

有关更多信息,请参见http://spot.colorado.edu/~reids/papers/how-scala-experience-improved-our-java-development-reid-2011.pdf

这是创建Vaadin按钮的示例:

val button = new Button("Click me"){
 setWidth("20px")
 setDescription("Click on this")
 setIcon(new ThemeResource("icons/ok.png"))
}

3

import声明中排除成员

假设您要使用Logger包含printlnprinterr方法的,但只希望将其用于错误消息,而将旧的保留Predef.println为标准输出。您可以这样做:

val logger = new Logger(...)
import logger.printerr

但是如果logger还包含您要导入和使用的另外十二种方法,则列出它们很不方便。您可以尝试:

import logger.{println => donotuseprintlnt, _}

但这仍然“污染”了导入成员的列表。输入超级强大的通配符:

import logger.{println => _, _}

这将做正确的事 ™。


2

require方法(在中定义Predef),使您可以定义在运行时将检查的其他功能约束。想象一下,您正在开发另一个Twitter客户端,并且您需要将tweet长度限制为最多140个符号。此外,您不能发布空的推文。

def post(tweet: String) = {
  require(tweet.length < 140 && tweet.length > 0) 
  println(tweet)
 }

现在使用不适当的length参数调用post将导致异常:

scala> post("that's ok")
that's ok

scala> post("")
java.lang.IllegalArgumentException: requirement failed
    at scala.Predef$.require(Predef.scala:145)
    at .post(<console>:8)

scala> post("way to looooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooong tweet") 
java.lang.IllegalArgumentException: requirement failed
    at scala.Predef$.require(Predef.scala:145)
    at .post(<console>:8)

您可以编写多个需求,甚至可以为每个需求添加描述:

def post(tweet: String) = {
  require(tweet.length > 0, "too short message")
  require(tweet.length < 140, "too long message")
  println(tweet)
}

现在,异常很冗长:

scala> post("")
java.lang.IllegalArgumentException: requirement failed: too short message
    at scala.Predef$.require(Predef.scala:157)
    at .post(<console>:8)

这里还有一个例子。


奖金

每当需求失败时,您都可以执行操作:

scala> var errorcount = 0
errorcount: Int = 0

def post(tweet: String) = {
  require(tweet.length > 0, {errorcount+=1})
  println(tweet)
  }

scala> errorcount
res14: Int = 0

scala> post("")
java.lang.IllegalArgumentException: requirement failed: ()
    at scala.Predef$.require(Predef.scala:157)
    at .post(<console>:9)
...

scala> errorcount
res16: Int = 1

1
require不是保留字。但这只是在中定义的方法Predef
missingfaktor 2011年

1

具有abstract override方法的特性是Scala的一项功能,没有像其他许多功能那样广为宣传。使用abstract override修饰符的方法的目的是进行一些操作并将委托委托给super。然后,必须将这些特征与它们的abstract override方法的具体实现混合在一起。

trait A {
  def a(s : String) : String
}

trait TimingA extends A {
  abstract override def a(s : String) = {
    val start = System.currentTimeMillis
    val result = super.a(s)
    val dur = System.currentTimeMillis-start
    println("Executed a in %s ms".format(dur))
    result
  }
}

trait ParameterPrintingA extends A {
  abstract override def a(s : String) = {
    println("Called a with s=%s".format(s))
    super.a(s)
  }
}

trait ImplementingA extends A {
  def a(s: String) = s.reverse
}

scala> val a = new ImplementingA with TimingA with ParameterPrintingA

scala> a.a("a lotta as")
Called a with s=a lotta as
Executed a in 0 ms
res4: String = sa attol a

虽然我的示例实际上只不过是一个穷人的AOP,但我还是很喜欢使用这些Stackable Traits来构建具有预定义导入,自定义绑定和类路径的Scala解释器实例。该堆叠特征使得能够沿着线创建我的工厂new InterpreterFactory with JsonLibs with LuceneLibs,然后有有用的进口和范围变量下为用户脚本。

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.