变更日志

2.13.0 版本中的更改

请参阅 Scala 2.13.0 版本说明

2.12.0 版本中的更改

请参阅 Scala 2.12.0 版本说明

2.11 版本中的更改

请参阅 Scala 2.11.1 版本说明

2.10.0 版本中的更改

Scala 2.10.0 代码库包含以下新功能和更改

值类

现在,类可以扩展 AnyVal 以使其表现得像结构类型(有限制)。请参阅:https://docs.scala-lang.org.cn/overviews/core/value-classes.html

隐式类

现在,implicit 修饰符也适用于类定义,以减少隐式包装器的样板代码。请参阅:https://docs.scala-lang.org.cn/sips/implicit-classes.html

字符串插值

示例

val what = "awesome"; println(s"string interpolation is ${what.toUpperCase}!")

请参阅更多信息:https://docs.scala-lang.org.cn/overviews/core/string-interpolation.html

期货和承诺

异步获取一些 JSON

for (req <- WS.url(restApiUrl).get()) yield (req.json \ "users").as[List[User]] (uses play!)

请参阅更多信息:https://docs.scala-lang.org.cn/overviews/core/futures.html

动态和 applyDynamic

如果 x 的类型未定义 foo,但它是 Dynamic 的子类型,则 x.foo 将变为 x.applyDynamic("foo")。请参阅:https://docs.scala-lang.org.cn/sips/type-dynamic.html

依赖方法类型

def identity(x: AnyRef): x.type = x // the return type says we return exactly what we got

基于 ASM 的新字节码发射器

  • 可以针对 JDK 1.5、1.6 和 1.7
  • 默认情况下会发出 1.6 字节码
  • 旧的 1.5 后端已弃用

新的模式匹配器

  • 从头开始重写,以生成更健壮的代码(不再出现指数级爆炸!)
  • 代码生成和分析现在是独立的(后者可以使用 -Xno-patmat-analysis 关闭)

Scaladoc 改进

  • 隐式(-implicits 标志)
  • 图表(-diagrams 标志,需要 graphviz)
  • 组(-groups)

模块化语言功能

通过显式导入它们,了解代码库中使用的高级 Scala 功能。请参阅:https://docs.scala-lang.org.cn/sips/modularizing-language-features.html

现在可以使用自定义线程池配置并行集合

请参阅:https://docs.scala-lang.org.cn/overviews/parallel-collections/overview.html

Akka Actor 现在是发行版的一部分

原始的 Scala Actor 现在已弃用。有关更多信息,请参阅 Actor 迁移项目。

性能改进

  • 更快的内联器
  • Range#sum 现在为 O(1)
  • ForkJoin 库的更新
  • 不可变 TreeSet/TreeMap 的修复
  • PartialFunction 的改进
  • 添加 ???NotImplementedError
  • 添加 IsTraversableOnce + IsTraversableLike 类型类用于扩展方法
  • 弃用和清理
  • 浮点数和八进制字面量语法弃用
  • 删除 scala.dbc
  • 实验性功能

以下令人兴奋的 - 实验性的 - 功能是 2.10.0 的一部分

Scala 反射

参见:https://docs.google.com/document/d/1Z1VhhNPplbUpaZPIYdc0_EUv5RiGQ2X4oqp0i-vz1qw/edit#heading=h.pqwdkl

参见:https://docs.scala-lang.org.cn/overviews/macros/overview.html

该 API 在 2.11.x 系列中可能会发生(可能很大)的变化,但这并不能阻止你尝试使用它们!许多开发人员已经为它们想出了非常酷的应用程序。

版本 2.9.1 到 2.9.2 的更改

没有引入语言更改。

版本 2.9.0 (2011 年 5 月 12 日) 的更改 {: #2.9.0 }

Scala 2.9.0 代码库包含以下新功能和更改

并行集合

每个集合都可以使用新的 par 方法转换为相应的并行集合。并行集合通过并行实现批量操作(如 foreachmapfilter 等)来利用多核处理器。并行集合位于 scala.collection.parallel 包中。

根据所讨论的集合,par 可能需要复制底层数据集以创建并行集合。但是,特定集合与其并行集合共享底层数据集,使 par 成为一个常数时间操作。

当前可用的并行集合是

  • 并行数组 - scala.collection.parallel.mutable.ParArray
  • 并行范围 - scala.collection.parallel.immutable.ParRange
  • 并行哈希映射 - scala.collection.parallel.mutable.ParHashMap
  • 并行哈希集 - scala.collection.parallel.mutable.ParHashSet
  • 并行哈希尝试 - scala.collection.parallel.immutable.{ParHashMap, ParHashSet}
  • 并行向量 - scala.collection.parallel.immutable.ParVector

方法 seq 用于从并行集合转换为相应的顺序集合。此方法始终有效 (O(1))。

App 特性

App 特性是 Application 特性的更安全、更强大的替代方案,后者现已弃用。编写顶层应用程序的推荐方法如下

object Echo extends App {
  println("Echo" + (args mkString " "))
}

从旧的 Application 特性继承的对象几乎与编写起来一样方便,但不是线程安全的,并且通常不会被 VM 优化,因为应用程序的主体是在对象初始化序列的一部分中执行的。从 App 特性继承的对象使用 Scala 2.9 的延迟初始化功能,将整个主体作为继承的 main 方法的一部分执行。App 方案的另一个新功能是命令行参数现在可以通过 args 值访问(该值继承自 App 特性)

DelayedInit 特性

DelayedInit 特性提供了另一个工具来定制类和对象的初始化序列。如果一个类或对象继承了这个特性,它的所有初始化代码都将被封装在一个闭包中,并作为参数传递给一个名为 delayedInit 的方法,该方法在 DelayedInit 特性中定义为抽象方法。

DelayedInit 的实现因此可以自由地执行初始化代码。例如,Scala 的新 App 特性将所有初始化序列存储在一个内部缓冲区中,并在对象的 main 方法被调用时执行它们。

请注意,只有包含在类和对象中的初始化代码才会传递给 DelayedInit;包含在特性中的初始化代码不受影响。

REPL 改进

jline(REPL 输入处理程序)的改进。更强大的光标处理、bash 风格的 ctrl-R 历史搜索、新的命令,如 :imports:implicits:keybindings。在具有必要运行时支持的平台上,:javap 将反汇编任何类,包括 REPL 定义的类。现在可以通过 ctrl-C 中断长时间运行的 REPL 命令,而不会终止 REPL 会话。改进的可编程性:REPL 类加载器通过给定的名称公开 REPL 定义的类。

Scala 运行器

Scala 代码现在可以通过以下任何方式执行

  • scala <jarfile> 将运行主类,类似于 java -jar
  • scala <classname> 将运行该类的 main 方法
  • scala <sourcefile> 将运行脚本内容作为 Scala 脚本
  • scala <sourcefile> 将在内容不是脚本的情况下,在顶层对象中找到一个 main 方法并运行它。这允许同一个文件与 scalac 一起使用并直接运行。
  • scala -save <sourcefile> 将创建一个包含编译源代码的 jar 文件,该文件可重复使用,并可以作为 scala <jarfile> 运行

Java 互操作性

  • 现在支持 @strictfp 注解。
  • JavaConvertersJavaConversions 中进行了各种修复,以实现更顺畅的互操作性。
  • 原始类型及其包装版本现在可以双向隐式转换。

其他功能

广义的 try-catch-finally

try body
catch handler
finally cleanup

这里,bodycleanup 可以是任意表达式,handler 可以是任何表达式,该表达式计算结果为有效的异常处理程序(即:PartialFunction[Throwable, T])。

新包

scala.sysscala.sys.process,它们从 sbt.Process 导入。

集合中的新方法

collectFirstmaxByminByspaninitstailspermutationscombinationssubsets

AnyRef 特化

现在可以对 AnyRef 的子类型的类型参数进行特化(class Foo[@specialize(AnyRef) T](arr: Array[T]) { ... }),这允许更有效地进行数组索引和更新。

版本 2.8.1 中的更改

没有引入语言更改。

版本 2.8.0 中的更改 (2010 年 7 月 14 日) {: #2.8.0 }

Scala 2.8.0 是一个具有重大创新意义的版本,其中包含大量修复并引入了许多新功能

重新设计的集合库

Scala 2.8 的集合库经过了彻底的改造,提供更连贯、更高效的设计,同时保持与现有源代码的几乎完全兼容。详细信息 在此

新的数组实现,多态数组的清单

Scala 2.8 中简化并优化了对数组的处理。之前的编译器魔术已被更系统、更可预测的隐式转换实现所取代。详细信息 在此

类型特化

Scala 2.8 添加了专门的类型参数,使编译器能够透明地生成给定定义的多个版本,并在调用站点处的静态类型信息允许的情况下使用最具体的版本。详细信息 在此

命名参数和默认参数

命名参数提高了具有多个参数的方法调用的可读性。默认参数减少了代码重复,并为 case 类启用了“复制”方法,这对于快速生成修改后的 case 类副本很有用。详细信息 在此

包对象

除了类和对象之外,包现在还可以包含方法、字段或类型别名。这些通过声明包对象添加到包中。在后续版本中可能会向包对象添加更多功能。

增强了 Scala Swing 库,文档更完善

组件发布关键事件,输入事件可以被消费,重构的窗口子层次结构,额外的演示,Swing 监听器是延迟安装的,更完整的组件缓存,小幅重构,错误修复,更多 Scaladocs。详细信息 在此

改进的 REPL

许多错误修复。类路径上所有包的 Tab 补全,以及对象和实例方法和字段,包括类型别名和包对象。可搜索的历史记录,集成的 shell 访问,以及提供对编译器内部的直接访问的强力模式。

隐式更改

我们改进了隐式解析过程,以便解析现在能够确定类型变量。

改进的相等性

所有基本类型之间的数字类型相等性应保持一致,同时也要遵守 equals/hashCode 契约。数字比较将与 Java 基本类型之间的比较结果相同。

Packrat 解析器组合器

通过对 packrat 解析的支持,解析器组合器现在能够处理左递归语法,并且在处理歧义产生式时将显示出性能提升。

改进的 XML 库

许多错误修复。

类型构造函数推断

类型推断已扩展以处理类型构造函数,因此在某些情况下,您可以省略包含更高阶类型(也称为类型构造函数,例如 List)的类型参数列表。

改进的注释

Scala 2.8 添加了对嵌套 Java 注释的支持。对于字段上的注释,现在可以指定哪个合成成员(getter/setter)将具有该注释。有关 Scala 注释的文档,请参见 此处

增强型 Actor

新的 Reactor 提供了更轻量级、纯粹基于事件的 Actor,并具有可选的隐式发送者识别。添加了对具有守护进程风格语义的 Actor 的支持。可以配置 Actor 以使用高效的 JSR166y fork/join 池,从而在 1.6 JVM 上实现显著的性能改进。调度程序现在是可插拔的,并且更容易自定义。

对延续的支持

延续由编译器插件支持,该插件现在作为主发行版的一部分提供支持。

内部改进

新的演示编译器

这种新的基础设施位于 Scala 编译器中,使 IDE 能够挂钩到编译器以有效地查找有关正在编辑的程序结构的信息。此新代码为 IDE 插件的开发提供了更好的平台。

新的构建管理器

例如 Eclipse 使用的新功能,可以智能地检测文件中的更改,并仅编译必要的 Scala 源代码,而不是对整个项目执行干净构建。这种技术可以显着减少大型项目的编译时间。

速度改进

编译器现在以优化后的代码运行。此外,一些改进和微调进一步提高了编译器速度,最高可达 50%。

其他工具

Scaladoc 2

新的外观和感觉、自动注释扩展和类似维基的语法,以及编译时错误检查。

Sbaz 2

Sbaz 包含许多错误修复和增强功能。它现在在漫长的下载过程中以及诊断依赖项审核时为用户提供更好的反馈,这些审核已过时并得到增强。Sbaz 应该能够在使用 cmd 或 cygwin 的 Windows 上正常工作,并且现在能够可靠地更新自身。添加了对 pack200 的支持,在某些情况下,文件大小减少了高达 70%。

Scalap

社区贡献了一个新的 scalap。新的 scalap 能够识别包对象,并使用 <package_name>.package 对其进行反编译。

Scala IDE for Eclipse

IDE 进行了大量重构,许多功能被迁移到 Scala 编译器中,以便更好地维护和在非 Eclipse IDE 和其他工具中重复使用。与 Eclipse 的 JDT 的集成得到了加强,许多以前 Scala 特定的行为和功能现在由 JDT 直接提供,从而带来了全面的改进。Scala IDE for Eclipse 现在 托管在 Assembla 上

2.7.3 到 2.7.7 的更改

没有引入语言更改。

2.7.2 版本的更改 (2008 年 11 月 10 日) {: #2.7.2 }

泛型签名

Scala 编译器现在生成 Java 的泛型签名,以便 Java 可以看到 Scala 泛型。

Java/Scala 联合项目

编译器现在可以解析(但不能转换)Java 源文件。这使得拥有具有相互递归依赖关系的混合 Java/Scala 项目成为可能。在这样的项目中,您可以首先将所有 Java 和 Scala 源代码提交给 Scala 编译器。在第二步中,使用 Scala 生成的 .class 文件编译 Java 源代码,并使用 Java 生成的 .class 文件再次编译 Scala 源代码。

ScalaSwing

另一个重大新增功能是 ScalaSwing 库的第一个测试版,它现在与发行版捆绑在一起。

Scala 集合

David MacIver 贡献了新的集合类实现:IntMapLongMapTreeHashMap(不可变),ArrayStack 和 OpenHashMap(可变)。

2.7.1 版本的更改 (2008 年 4 月 9 日) {: #2.7.1 }

类型中通配符占位符作用域规则的更改

类型中的通配符现在绑定到最接近的封闭类型应用。

例如,List[List[_]] 现在等效于存在类型

List[List[t] forSome { type t }]face

在 2.7.0 版本中,类型扩展为

List[List[t]] forSome { type t }

新的约定与 Java 中通配符的解释方式完全一致。

隐式不需要收缩性要求

隐式方法定义的收缩性要求已被删除。取而代之的是,对于每个隐式扩展,都会单独检查扩展是否会导致循环或无限增长的类型树。

2.7.0 版变更 (2008 年 2 月 7 日) {: #2.7.0 }

Java泛型

Scala 现在默认支持 Java 泛型

  • Java 中的泛型类型,例如 ArrayList<String>,被转换为 Scala 中的泛型类型:ArrayList[String]
  • 通配符类型,例如 ArrayList<? extends Number>,被转换为 ArrayList[_ <: Number]。这本身是存在类型 ArrayList[T] forSome { type T <: Number } 的简写。
  • Java 中的原始类型,例如 ArrayList,被转换为 ArrayList[_],这是 ArrayList[T] forSome { type T } 的简写。

如果指定了 -target:jvm-1.5,则此转换有效,这是新的默认设置。对于任何其他目标,Java 泛型都不会被识别。为了确保 Scala 代码库的可升级性,在 scalac -target:jvm-1.4 下,Java 类中的多余类型参数将被简单地忽略。例如,在使用 >-target:jvm-1.4 编译时,Scala 类型,例如 ArrayList[String],将被简单地视为未参数化的类型 ArrayList>。

案例类

Scala 编译器现在为每个案例类生成一个伴随提取器对象。例如,给定案例类

case class X(elem: String)

将生成以下伴随对象

object X {
  def unapply(x: X): Some[String] = Some(x.elem)
  def apply(s: String): X = new X(s)
}

如果对象已经存在,则只向其中添加 applyunapply 方法。

案例类的三个限制已被删除

  • 案例类现在可以从其他案例类继承。

    case class Foo(x: Int)
    case class Bar(override val x: Int, y: Int) extends Foo(x)
    object test extends Application {
      println(Bar(1, 2).x)
      (Bar(1, 2): Foo) match {
        case Foo(x) => println(x)
      }
    }
    
  • 案例类现在可以是 abstract
  • 案例类现在可以带有伴随对象

    case class Foo(x: Int)
    object Foo {
      val x = 2
      val y = Foo(2)
    }
    object test extends Application {
      println(Foo.x)
      println(Foo.y match { case Foo(x) => x } )
    }
    

已删除的功能

以下已弃用的功能已从标准 Scala 库中删除

已删除 改用
AllAllRef(对象 scala.Predef NothingNull(从 2.3.0 版本开始可用)
element 和 arity(类 scala.Product productElementproductArity
scala.compat.Math scala.Math
scala.testing.UnitTest scala.testing.SUnit
assertNotSameassertSame(类 scala.testing.SUnit.Assert assertNotEqassertEq
scala.util.Fluid scala.util.DynamicVariable

2.6.1 版本中的更改(2007 年 11 月 30 日){: #2.6.1 }

模式绑定引入的可变变量

现在可以通过模式匹配定义引入可变变量,就像值一样。例如

var (x, y) = if (positive) (1, 2) else (-1, -3)
var hd :: tl = mylist

自类型

现在可以引入自类型,而无需为 this 定义别名。例如

class C {
  type T <: Trait
  trait Trait { this: T => ... }
}

2.6 版本中的更改(2007 年 7 月 27 日){: #2.6.0 }

存在类型

现在可以使用新的关键字 forSome 定义存在类型。存在类型具有以下形式 T forSome {Q},其中 Q 是值和/或类型声明的序列。给定类定义

class Ref[T]
abstract class Outer { type T }

例如,可以编写以下存在类型

Ref[T] forSome { type T <: java.lang.Number }
Ref[x.T] forSome { val x: Outer }

惰性值

现在可以使用新的修饰符 lazy 定义惰性值声明。

import compat.Platform._
val t0 = currentTime
lazy val t1 = currentTime
val t2 = currentTime

println("t0 <= t2: " + (t0 <= t2))  //true
println("t1 <= t2: " + (t1 <= t2))  //false (lazy evaluation of t1)

结构类型

现在可以使用类型细化来声明结构类型。例如

class File(name: String) {
  def getName(): String = name
  def open() { /*..*/ }
  def close() { println("close file") }
}
def test(f: { def getName(): String }) { println(f.getName) }

test(new File("test.txt"))
test(new java.io.File("test.txt"))

已弃用的功能

  • for 循环的旧式语法已弃用。
  • requires 子句已弃用;改用 { self: T =>; ... }
  • &f 用于未应用方法已弃用;请使用 f _ 代替。

版本 2.5(2007 年 5 月 2 日)中的更改 {: #2.5.0 }

类型构造函数多态性

类型参数和抽象类型成员现在也可以抽象化类型构造函数。这允许更精确的 Iterable 接口

trait Iterable[+t] {
  type MyType[+t] <: Iterable[t] // MyType is a type constructor

  def filter(p: t => Boolean): MyType[t] = //...
  def map[s](f: t => s): MyType[s] = //...
}

abstract class List[+t] extends Iterable[t] {
  type MyType[+t] = List[t]
}

Iterable 定义明确说明,将函数映射到特定结构(例如,List>)将产生相同的结构(包含不同的元素)。

早期对象初始化

现在可以在调用任何父构造函数之前初始化对象的某些字段。这对于没有正常构造函数参数的特征特别有用。例如

trait Greeting {
  val name: String
  val msg = "How are you, " + name
}
class C extends {
  val name = "Bob"
} with Greeting {
  println(msg)
}

在上面的代码中,字段 name 在调用 Greeting 的构造函数之前初始化。因此,类 Greeting 中的字段 msg 正确初始化为 "How are you, Bob"

for 理解,修订版

for 理解的语法已更改。例如

for (val x <- List(1, 2, 3); x % 2 == 0) println(x)

现在写成

for (x <- List(1, 2, 3) if x % 2 == 0) println(x)

因此,for 理解现在以(可能受保护的)生成器开头,后跟一个或多个枚举器,这些枚举器可以是(可能受保护的)生成器、保护器或局部值定义。

旧语法仍然可用,但将在将来弃用。

隐式匿名函数

现在可以使用参数位置的下划线来定义匿名函数。例如,左侧列中的表达式都是函数值,它们扩展到右侧的匿名函数。

_ + 1                  x => x + 1
_ * _                  (x1, x2) => x1 * x2
(_: int) * 2           (x: int) => (x: int) * 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)

作为特例,部分未应用方法现在被指定为 m _ 而不是以前的符号 &;m

新符号将取代特殊语法形式 .m()(用于抽象化方法接收器)和 &m(用于将未应用方法视为函数值)。目前,旧语法形式仍然可用,但将在将来弃用。

模式匹配匿名函数,改进

现在可以使用 case 语句直接为大于 1 元的函数定义函数值。以前,只有单目函数可以这样定义。例如

def scalarProduct(xs: Array[Double], ys: Array[Double]) =
  (0.0 /: (xs zip ys)) {
    case (a, (b, c)) => a + b * c
  }

2.4 版(2007 年 3 月 9 日)中的更改 {: #2.4.0 }

对象局部私有和受保护

现在,privateprotected 修饰符接受 [this] 限定符。标记为 private[this] 的定义 M 是私有的,此外只能从当前对象内部访问。也就是说,M 的唯一合法前缀是 thisC.this。类似地,标记为 protected[this] 的定义 M 是受保护的,此外只能从当前对象内部访问。

元组,修订版

元组的语法已从 {...} 更改为 (...)。对于任何类型序列 T1,…,Tn

(T1, …, Tn)Tuplen[T1, …, Tn] 的简写。

类似地,对于任何表达式或模式序列 x1,…,xn

(x1, …, xn)Tuplen(x1, …, xn) 的简写。

主构造函数的访问修饰符

类的主构造函数现在可以标记为 privateprotected。如果给出这样的访问修饰符,它将位于类名及其值参数之间。例如

class C[T] private (x: T) { ... }

注释

对属性的支持已扩展,其语法也已更改。属性现在称为注释。语法已更改为遵循 Java 的约定,例如 @annotation 而不是 [attribute]。旧语法仍然可用,但将来会弃用。

注释现在已序列化,以便编译时或运行时工具可以读取它们。类 scala.Annotation 有两个子特征,用于指示注释的保留方式。继承自特征 scala.ClassfileAnnotation 的注释类的实例将存储在生成的类文件中。继承自特征 scala.StaticAnnotation 的注释类的实例将对访问注释符号的每个编译单元中的 Scala 类型检查器可见。

可判定子类型化

子类型化的实现已更改,以防止无限递归。子类型化的终止现在通过对类图的新的有限性限制来确保。

案例类不能是抽象的

现在明确禁止案例类是抽象的。规范以前对此没有说明,但没有解释如何处理抽象案例类。Scala 编译器允许这种用法。

自我别名和自我类型的新的语法

现在可以为自引用 this 明确指定别名和/或类型。例如,在

class C { self: D =>
  ...
}

中,名称 self 被引入作为 Cthis 的别名,并且 C 的自类型被假定为 D。现在引入这种结构是为了最终替换限定的 this 结构 C.this 和 Scala 中的 requires 子句。

赋值运算符

现在可以将运算符与赋值结合起来。例如

var x: int = 0
x += 1

2.3.2 版本中的更改 (2007 年 1 月 23 日) {: #2.3.2 }

提取器

现在可以使用提取器对象中的 unapply 方法独立于 case 类定义模式。以下是一个示例

object Twice {
  def apply(x: Int): int = x*2
  def unapply(z: Int): Option[int] = if (z%2 == 0) Some(z/2) else None
}
val x = Twice(21)
x match { case Twice(n) => Console.println(n) } // prints 21

在示例中,Twice 是一个具有两个方法的提取器对象

  • apply 方法用于构建偶数。
  • unapply 方法用于分解偶数;从某种意义上说,它是 apply 的反向操作。 unapply 方法返回选项类型:匹配成功时为 Some(...),匹配失败时为 None。模式变量作为 Some 的元素返回。如果有多个变量,则将它们分组到元组中。

在倒数第二行中,Twiceapply 方法用于构造一个数字 x。在最后一行中,x 与模式 Twice(n) 进行比较。此模式对偶数有效,并将被测试数字的一半分配给变量 n。模式匹配利用了对象 Twiceunapply 方法。有关提取器的更多详细信息,请参阅 Emir、Odersky 和 Williams 的论文 Matching Objects with Patterns

元组

引入了元组的新轻量级语法。对于任何类型序列 {T1 ,.., Tn}

{T1 ,.., Tn}Tuplen[T1 ,.., Tn] 的简写。

类似地,对于任何表达式或模式序列 x1,.., xn

{x1 ,.., xn}Tuplen(x1 ,.., xn) 的简写。

更高元数的中缀运算符

现在可以使用具有多个参数的方法作为中缀运算符。在这种情况下,所有方法参数都将作为括号中的普通参数列表写入。示例

class C {
  def +(x: int, y: String) = ...
}
val c = new C
c + (1, "abc")

已弃用属性

现在可以使用新的标准属性 deprecated。如果成员定义用此属性标记,则对该成员的任何引用都将导致发出“已弃用”警告消息。

2.3.0 版(2006 年 11 月 23 日)中的更改 {: #2.3.0 }

过程

引入了返回 Unit 的函数的简化语法。Scala 现在允许以下简写

def f(params) 用于 def f(params): Unit 以及

def f(params) { … } 用于 def f(params): Unit = { … }

类型模式

模式中类型的语法已得到改进。Scala 现在区分类型变量(以小写字母开头)和模式中作为类型参数的类型。类型变量在模式中绑定。其他类型参数与以前版本一样被擦除。Scala 编译器现在将在类型擦除可能损害类型安全的地方发出“unchecked”警告。

标准类型

Scala 类型层次结构中两个底部类的推荐名称已更改如下

All      ==>     Nothing
AllRef   ==>     Null

旧名称仍然可用作类型别名。

2.1.8 版(2006 年 8 月 23 日)中的更改 {: #2.1.8 }

受保护的可见性限定符

受保护的成员现在可以具有可见性限定符,例如 protected[<qualifier>]。特别是,现在可以像在 Java 中编写那样模拟包受保护的访问

protected[P] def X ...

其中 P 将命名包含 X 的包。

私有访问的放松

类的私有成员现在可以从类的伴生模块中引用,反之亦然。

隐式查找

隐式定义的查找方法已得到概括。当搜索与类型 T 匹配的隐式定义时,现在将考虑

  • 所有无需前缀即可访问的标识符,以及
  • T 关联的类的伴生模块的所有成员。

(第二条子句比以前更通用)。在这里,如果某个类被 T 的某些部分引用,或者它是 T 的某些部分的基类,则该类与类型 T 关联。例如,要查找与类型相对应的隐式成员

HashSet[List[Int], String]

现在需要查看 HashSetListIntString 的配套模块(也称为静态部分)。之前,只查看 HashSet 的静态部分。

收紧模式匹配

使用单例类型 p.type 的类型化模式匹配现在会测试选择器值是否与 p 引用相等。例如

val p = List(1, 2, 3)
val q = List(1, 2)
val r = q
r match {
  case _: p.type => Console.println("p")
  case _: q.type => Console.println("q")
}

这将匹配第二种情况,因此将打印 "q"。之前,单例类型会被擦除为 List,因此第一种情况会匹配,这是没有意义的。

2.1.7 版本中的更改 (19-Jul-2006) {: #2.1.7 }

多行字符串字面量

现在可以使用三引号括起来编写多行字符串字面量。示例

"""this is a
   multi-line
   string literal"""

在这样的字符串字面量中,除了 unicode 转义之外,不会执行任何转义替换。

2.1.5 版本中的更改 (24-May-2006) {: #2.1.5 }

类字面量

类字面量有新的语法:对于任何类类型 CclassOf[C] 表示 C 的运行时表示。

2.0 版本中的更改 (12-Mar-2006) {: #2.0 }

Scala 的第二个版本在一些细节上与该语言的第一个版本不同。已经添加了一些功能,并且一些旧的习惯用法不再受支持。本节总结了主要更改。

新关键字

以下三个词现在是保留词;它们不能用作标识符

implicit     match     requires

换行符作为语句分隔符

现在可以使用换行符作为语句分隔符来代替分号

语法限制

在某些其他情况下,旧的结构不再起作用

模式匹配表达式

match 关键字现在只作为选择器表达式和多个情况之间的中缀运算符出现,如

expr match {
  case Some(x) => ...
  case None => ...
}

不再支持 expr.match {...} 或仅 match {...} 等变体。

withextends 子句中

该习语

class C with M { ... }

不再受支持。一个 with 连接词只能在 extends 子句之后使用。例如,上面的行必须写成

class C extends AnyRef with M { ... }

但是,假设 M 是一个特征,写成下面这样也是合法的

class C extends M { ... }

后一个表达式被视为等同于

class C extends S with M { ... }

其中 SM 的超类。

正则表达式模式

目前唯一支持的正则表达式模式是序列模式,它可能以序列通配符 _* 结尾。例如

case List(1, 2, _*) => ... // will match all lists starting with 1,2

目前尚不清楚这是否是永久限制。我们正在评估在 Scala 中重新引入完整正则表达式模式的可能性。

自类型注解

自类型注解的推荐语法已更改。

class C: T extends B { ... }

变成

class C requires T extends B { ... }

也就是说,自类型现在由新的 requires 关键字表示。旧语法仍然可用,但被认为已弃用。

for 循环

for 循环现在允许值和模式定义。例如

for {
  val x <- List.range(1, 100)
  val y <- List.range(1, x)
  val z = x + y
  isPrime(z)
} yield Pair(x, y)

请注意 for 循环中的第三项定义 val z = x + y

转换

方法到函数的隐式转换规则已收紧。以前,用作值的带参数方法总是隐式转换为函数。当方法参数被遗忘时,这会导致意外结果。例如,考虑下面的语句

show(x.toString)

其中 show 定义如下

def show(x: String) = Console.println(x)

很可能,程序员忘记为 toString 提供一个空参数列表 ()。以前的 Scala 版本会将此代码视为部分应用方法,并将其扩展为

show(() => x.toString())

因此,将打印闭包的地址,而不是 s 的值。

Scala 版本 2.0 仅当表达式的预期类型确实是函数类型时,才会应用从部分应用方法到函数值的转换。例如,在上面的代码中不会应用转换,因为 show 参数的预期类型是 String,而不是函数类型。

新的约定不允许一些以前合法的代码。例如

def sum(f: int => double)(a: int, b: int): double =
  if (a > b) 0 else f(a) + sum(f)(a + 1, b)

val sumInts = sum(x => x)  // error: missing arguments

上面代码最后一行中 sum 的部分应用不会转换为函数类型。相反,编译器将生成一条错误消息,指出方法 sum 的参数缺失。可以通过为部分应用提供预期类型来解决此问题,例如,通过使用其类型注释 sumInts 的定义

val sumInts: (int, int) => double = sum(x => x)  // OK

另一方面,Scala 版本 2.0 现在会在必要时自动将空参数列表的方法应用于 () 参数列表。例如,上面的 show 表达式现在将扩展为

show(x.toString())

Scala 2.0 版本放宽了关于重写方法的规则,特别是针对空参数列表的情况。修改后的“匹配成员”定义现在允许用显式但为空的参数列表 () 的方法重写无参数方法,反之亦然。例如,以下类定义现在是合法的

class C {
  override def toString: String = //...
}

之前,这个定义会被拒绝,因为从 java.lang.Object 继承的 toString 方法接受一个空参数列表。

类参数

类参数现在可以以 valvar 为前缀。

私有限定符

以前,Scala 有三个可见性级别:privateprotectedpublic。没有办法像 Java 一样限制对当前包中成员的访问。Scala 2 现在定义了访问限定符,允许表达这种可见性级别,以及其他级别。在以下定义中

private[C] def f(...)

f 的访问仅限于类或包 C 中的所有代码(其中必须包含 f 的定义)。

混合模型中的变化

详细说明类混合组合的模型已经发生了重大变化。主要区别在于

  • 我们现在区分用作混合类的特质和普通类。特质的语法从 1.0 版本进行了推广,现在允许特质具有可变字段。但是,与 1.0 版本一样,特质仍然不能具有构造函数参数。
  • 成员解析和 super 访问现在都根据类线性化定义。
  • Scala 的方法重载概念已经推广;特别是,现在可以在子类和超类中,或在几个不同的混合类中,拥有相同方法的重载变体。这使得 Scala 中的方法重载在概念上与 Java 中相同。

新的混合模型在 Scala 语言规范 中有更详细的解释。

隐式参数

Scala 1.0 中的视图已被更通用的隐式参数概念取代

模式匹配的灵活类型

Scala 的新版本在对异构类层次结构进行模式匹配时实现了更灵活的类型规则。异构类层次结构是指子类继承具有不同参数类型的公共超类的层次结构。在 Scala 2.0 版本的新规则下,可以对这种层次结构执行模式匹配,并使用更精确的类型,这些类型会跟踪通过比较选择器和匹配模式的类型而获得的信息。这使得 Scala 具有类似于带保护的代数数据类型的功能。