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"

如果您不习惯使用模式匹配和提取器,那么第二行看起来很混乱。每当您定义valvar 时,关键字后面的内容不仅仅是一个标识符,而是一个模式。这就是它起作用的原因:

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

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

大多数时候,您的模式使用作为单例对象成员的提取器。例如,如果你写一个像

这样的模式
Some(value)

然后你隐式调用提取器Some.unapply

但是您也可以在模式中使用类实例,这就是这里发生的事情。 val 正则表达式是Regex 的一个实例,当您在模式中使用它时,您会隐式调用regex.unapplySequnapplyunapplySeq 超出了此答案的范围),它会提取匹配组到 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: 将在某个编译器阶段之后打印语法树。要查看可用的阶段,请使用 scalac -Xshow-phases

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

试试

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: =&gt; Boolean)(t: =&gt; 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 子句之前。这保证 valueAbstractT2 的主体被执行,如 运行脚本时显示。

【讨论】:

该构造称为“早期初始化”。【参考方案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 的 Streams 构建无限数据结构: 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 语句中排除成员

假设您想使用包含printlnprinterr 方法的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的隐藏特性的主要内容,如果未能解决你的问题,请参考以下文章

Scala函数特性

scala高级特性-01

Scala的高级特性

Scala学习——高级特性

Scala的高级特性,实操演习

Scala中的语言特性是如何实现的?