Scala的隐藏特性
Posted
技术标签:
【中文标题】Scala的隐藏特性【英文标题】:Hidden features of Scala 【发布时间】:2009-06-22 01:05:03 【问题描述】:每个 Scala 开发人员都应该了解的 Scala 隐藏特性是什么?
请每个答案一个隐藏功能。
【问题讨论】:
嘿,这个问题与问题本身一样有用,因为它链接到其他隐藏功能帖子。干杯! @mettadore 只需查看右侧的相关链接即可。 @JohnMetta:或者使用tag。 【参考方案1】:好的,我必须再添加一个。 Scala 中的每个 Regex
对象都有一个提取器(参见上面 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"
如果您不习惯使用模式匹配和提取器,那么第二行看起来很混乱。每当您定义val
或var
时,关键字后面的内容不仅仅是一个标识符,而是一个模式。这就是它起作用的原因:
val (a, b, c) = (1, 3.14159, "Hello, world")
右手表达式创建一个Tuple3[Int, Double, String]
,它可以匹配模式(a, b, c)
。
大多数时候,您的模式使用作为单例对象成员的提取器。例如,如果你写一个像
这样的模式Some(value)
然后你隐式调用提取器Some.unapply
。
但是您也可以在模式中使用类实例,这就是这里发生的事情。 val 正则表达式是Regex
的一个实例,当您在模式中使用它时,您会隐式调用regex.unapplySeq
(unapply
与unapplySeq
超出了此答案的范围),它会提取匹配组到 Seq[String]
中,其中的元素按顺序分配给变量年、月和日。
【讨论】:
感谢您发布此内容!仅供参考,在第一版第 503 页和第二版第 611 页的“Scala 编程”一书中的“使用正则表达式提取”一章中提到。【参考方案2】:Structural 类型定义 - 即由它支持的方法描述的类型。例如:
object Closer
def using(closeable: def close(): Unit , f: => Unit)
try
f
finally closeable.close
注意参数closeable
的类型除了有一个close
方法外没有定义
【讨论】:
在“Scala 编程”中甚至没有提到结构类型。尽管它们使用反射来调用正确的方法,但它们比其他传递类型的技术要慢一些。 (希望他们能想出一种方法来加快速度。) 而且还可以为它们创建别名,就像外部分配的接口(非常慢):type Closeable = def close(): Unit 【参考方案3】:Type-Constructor Polymorphism(又名更高种类的类型)
例如,如果没有此功能,您可以表达将函数映射到列表以返回另一个列表,或将函数映射到树以返回另一棵树的想法。但是你不能一般地在没有更高种类的情况下表达这个想法。
对于更高的种类,您可以捕捉到 任何类型 的概念,即用另一种类型进行参数化。接受一个参数的类型构造函数称为(*->*)
。例如,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
上映射一个函数。
【讨论】:
【参考方案4】:Extractors 允许您用模式替换凌乱的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)
【讨论】:
这不是开关吗?也许这可以重构更多。 模式就像涡轮增压开关:更加强大和清晰 很好,但我不喜欢你必须使用隐式,因为它的范围比匹配更远。您也可以向 ProductService 添加一个通过代码查找 Product 的方法。无论如何,您都可以将重构的 sn-p 包装在一个方法中,以便能够在任何地方使用它。【参考方案5】:Manifests 这是一种在运行时获取类型信息的方法,就好像 Scala 已经具体化了类型一样。
【讨论】:
我认为最好在答案中解释答案in,而不是参考链接。顺便说一句,嗨再次牛弓! :-) 这是一个真正隐藏的功能……甚至在 API 文档中也没有。不过非常有用。【参考方案6】:在 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)
【讨论】:
【参考方案7】:案例类自动混合 Product trait,提供对字段的无类型、索引访问,无需任何反射:
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))
【讨论】:
【参考方案8】:它并不是完全隐藏的,但肯定是一个宣传不足的功能: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
,这是 Predef.scala 中 implicit def augmentString
的应用程序。
scalac -Xprint:
这是了解幕后情况的好方法。
试试
case class X(a:Int,b:String)
使用 typer 阶段来真正感受它的用处。
【讨论】:
【参考方案9】:您可以定义自己的控制结构。它实际上只是函数和对象以及一些语法糖,但它们的外观和行为就像真实的东西。
例如,下面的代码定义了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)
【讨论】:
更多示例:programmers.stackexchange.com/questions/13072/… 我很好奇是否有人知道一种方法来定义 if-then-else 块与可选的 else 像标准的类型检查一样。 @Philippe:zif[A : Zero](cond: => Boolean)(t: => A): A = if(cond) t else mzero
。需要 Scalaz。【参考方案10】:
Scala 2.8 中的@switch
注释:
要应用于匹配的注释 表达。如果存在,编译器 将验证匹配已 编译到 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
【讨论】:
【参考方案11】:不知道这是否真的隐藏,但我觉得很好。
接受 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
【讨论】:
不错!我可以想象这有时对提高可读性很有用。例如var foo2barConverter: Foo ConvertTo Bar
会使类型参数的顺序不言自明。
我有时会在某种程度上使用 PartialFunction 的代码中这样做:键入 ~>[A, B] = PartialFunction[A, B]【参考方案12】:
Scala 2.8 引入了默认参数和命名参数,这使得 Scala 添加到案例类的新“复制”方法成为可能。如果你定义这个:
case class Foo(a: Int, b: Int, c: Int, ... z:Int)
如果你想创建一个新的 Foo 就像现有的 Foo 一样,只是具有不同的“n”值,那么你可以说:
foo.copy(n = 3)
【讨论】:
警告:如果您从另一个案例类继承一个案例类,则不会覆盖复制方法。所以你必须手动覆盖它 相关:更新嵌套结构的更简洁方法***.com/q/3900307/203968 案例类不再(Scala 2.8)允许从案例类继承。感谢 Scala 之主弃用了这种邪恶的继承。【参考方案13】:在 Scala 2.8 中,您可以将 @specialized 添加到您的通用类/方法中。这将为原始类型创建特殊版本的类(扩展 AnyVal)并节省不必要的装箱/拆箱成本:
class Foo[@specialized T]...
您可以选择 AnyVals 的子集:
class Foo[@specialized(Int,Boolean) T]...
【讨论】:
您是否可以指出一个更长的解释?我想了解更多。【参考方案14】:扩展语言。我一直想在 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)
【讨论】:
【参考方案15】:您可以为函数指定一个按名称调用的参数(已编辑:这与惰性参数不同!),并且在函数使用之前不会对其进行评估(编辑:实际上,它将在每个使用时间)。详情见this faq
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)”只是一个匿名函数,并且像任何其他函数一样被评估为函数。 "x: ()=>Bar" 定义 x 一个不带参数并返回 Bar 的函数。 x: => Bar 将 x 定义为按名称调用。更多详情请关注scala.sygneca.com/faqs/… 您显示的是按名称调用的参数。延迟参数尚未实现:lampsvn.epfl.ch/trac/scala/ticket/240【参考方案16】:您可以使用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
作为内联,它不会带来任何额外的开销。
【讨论】:
这在***.com/questions/3237727/…有更好的解释【参考方案17】:Early Initialization:
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
我们实例化一个匿名内部 类,初始化
value
字段 在块中,在with AbstractT2
子句之前。这保证value
在AbstractT2
的主体被执行,如 运行脚本时显示。
【讨论】:
该构造称为“早期初始化”。【参考方案18】:您可以使用 '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
【讨论】:
【参考方案19】:匿名函数的占位符语法
来自 Scala 语言规范:
SimpleExpr1 ::= '_'
一个表达式(句法类别
Expr
)可以在标识符合法的地方包含嵌入的下划线符号_
。这样的表达式表示一个匿名函数,其中后续出现的下划线表示连续的参数。
来自Scala Language Changes:
_ + 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))
【讨论】:
这应该被称为“匿名函数的占位符语法”。隐式在 Scala 中有不同的含义,与此无关。 链接与答案的关系不明显。 “隐式”不是这个的正确术语。如上所述,它应该是“占位符”。 它并不是真正的“隐藏”,我在我读过的几乎所有关于 Scala 的教程中都看到了这种用法...... :-) 但我很欣赏我还没有看到的正式定义。 @PhiLho 也许它在 2009 年不太为人所知。我不知道。 我错过了原始日期,因为只显示了最后一次编辑日期。而且,并非此线程中解释的所有功能都是“隐藏的”。无论如何,很酷的线程和好的答案。【参考方案20】:隐式定义,尤其是转换。
例如,假设一个函数将格式化输入字符串以适应大小,方法是将其中间替换为“...”:
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
您可以将它与任何字符串一起使用,当然,也可以使用 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
现在您可以通过双精度调用该函数:
sizeBoundedString(12345.12345D, 8)
最后一个参数是隐式的,并且由于隐式 de 声明而被自动传递。此外,“s”在 sizeBoundedString 内被处理为字符串,因为从它到字符串的隐式转换。
这种类型的隐式更好地定义为不常见的类型,以避免意外的转换。你也可以显式传递一个转换,它仍然会在 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 的实例上将日志发送到守护程序 X 守护程序。但是在 X 的实例上调用 g 会将日志发送到显式给定的 DefaultDaemon。
虽然这个简单的示例可以用重载和私有状态重写,但隐式不需要私有状态,并且可以通过导入进入上下文。
【讨论】:
【参考方案21】:也许不是太隐蔽,但我认为这很有用:
@scala.reflect.BeanProperty
var firstName:String = _
这将自动为匹配 bean 约定的字段生成 getter 和 setter。
developerworks的进一步说明
【讨论】:
如果你经常使用它,你可以为它制作快捷方式,例如:import scala.reflect.BeanProperty => BP【参考方案22】:闭包中的隐式参数。
函数参数可以像方法一样被标记为隐式。在函数体的范围内,隐式参数是可见的并且有资格进行隐式解析:
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")
)
【讨论】:
【参考方案23】:使用 Scala 的 Stream
s 构建无限数据结构:
http://www.codecommit.com/blog/scala/infinite-lists-for-the-finitely-patient
【讨论】:
【参考方案24】:结果类型取决于隐式解析。这可以给你一种多分派的形式:
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
,在执行这些命令之前,该a
必须存在于环境中。我猜你的意思是z.perform(x)
。【参考方案25】:
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"))
【讨论】:
【参考方案26】:从import
语句中排除成员
假设您想使用包含println
和printerr
方法的Logger
,但您只想将一个用于错误消息,并保留旧的Predef.println
用于标准输出。你可以这样做:
val logger = new Logger(...)
import logger.printerr
但如果logger
还包含另外十二个您想要导入和使用的方法,则列出它们变得不方便。你可以试试:
import logger.println => donotuseprintlnt, _
但这仍然“污染”了导入成员的列表。输入超强大的通配符:
import logger.println => _, _
这将做正确的事情™。
【讨论】:
【参考方案27】:require
方法(在Predef
中定义)允许您定义将在运行时检查的其他函数约束。想象一下,您正在开发另一个 Twitter 客户端,并且您需要将推文长度限制为最多 140 个符号。此外,您不能发布空推文。
def post(tweet: String) =
require(tweet.length < 140 && tweet.length > 0)
println(tweet)
现在使用不适当的长度参数调用 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)
还有一个例子是here。
奖金
您可以在每次要求失败时执行操作:
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
【讨论】:
require
不是保留字。它只是Predef
中定义的一种方法。【参考方案28】:
具有 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 解释器实例。 Stackable Traits 可以按照new InterpreterFactory with JsonLibs with LuceneLibs
的方式创建我的工厂,然后为用户脚本提供有用的导入和范围变量。
【讨论】:
以上是关于Scala的隐藏特性的主要内容,如果未能解决你的问题,请参考以下文章