顶级定义

编译单元

CompilationUnit  ::=  {‘package’ QualId semi} TopStatSeq
TopStatSeq       ::=  TopStat {semi TopStat}
TopStat          ::=  {Annotation} {Modifier} TmplDef
                   |  Import
                   |  Packaging
                   |  PackageObject
                   |
QualId           ::=  id {‘.’ id}

编译单元由一系列打包、导入语句以及类和对象定义组成,这些定义可能以一个包语句开头。

一个编译单元

package ´p_1´;
...
package ´p_n´;
´\mathit{stats}´

以一个或多个包语句开头,等同于一个编译单元,该编译单元包含打包

package ´p_1´ { ...
  package ´p_n´ {
    ´\mathit{stats}´
  } ...
}

每个编译单元都隐式导入以下包,按给定顺序

  1. java.lang
  2. scala,以及
  3. 对象 scala.Predef,除非存在显式顶级导入引用 scala.Predef

按此顺序,后面导入的成员会隐藏前面导入的成员。

scala.Predef 的隐式导入的例外情况可能很有用,例如,隐藏预定义的隐式转换。

打包

Packaging       ::=  ‘package’ QualId [nl] ‘{’ TopStatSeq ‘}’

一个是一个特殊的对象,它定义了一组成员类、对象和包。与其他对象不同,包不是由定义引入的。相反,包的成员集是由打包决定的。

打包 package ´p´ { ´\mathit{ds}´ } 将 ´\mathit{ds}´ 中的所有定义作为成员注入到限定名为 ´p´ 的包中。包的成员称为顶级定义。如果 ´\mathit{ds}´ 中的定义被标记为 private,则它仅对包中的其他成员可见。

在打包内部,包 ´p´ 的所有成员在它们的简单名称下都是可见的。但是,此规则不扩展到 ´p´ 的封闭包的成员,这些成员由 ´p´ 路径的前缀指定。

package org.net.prj {
  ...
}

org.net.prj 中的所有成员在其简单名称下可见,但包 orgorg.net 中的成员需要显式限定或导入。

从 ´p´ 中选择 ´p´.´m´ 以及从 ´p´ 中导入,与对象的操作相同。但是,与其他对象不同,包不能用作值。具有与模块或类相同的完全限定名称的包是非法的。

包装之外的顶层定义被假定为注入到一个特殊的空包中。该包不能命名,因此不能导入。但是,空包的成员彼此可见,无需限定。

包对象

PackageObject   ::=  ‘package’ ‘object’ ObjectDef

一个包对象 package object ´p´ extends ´t´ 将模板 ´t´ 的成员添加到包 ´p´ 中。每个包只能有一个包对象。标准命名约定是在与包 ´p´ 相对应的目录中放置一个名为 package.scala 的文件,其中包含上述定义。

包对象不应定义与包 ´p´ 中定义的任何顶层对象或类同名的成员。如果存在名称冲突,程序的行为目前未定义。预计此限制将在 Scala 的未来版本中解除。

包引用

QualId           ::=  id {‘.’ id}

对包的引用采用限定标识符的形式。与所有其他引用一样,包引用是相对的。也就是说,从名称 ´p´ 开始的包引用将在定义名为 ´p´ 的成员的最近封闭范围内查找。

如果包名称被遮蔽,可以通过在它前面加上特殊的预定义名称 _root_ 来引用它的完全限定名称,该名称引用包含所有顶层包的最外层根包。

名称 _root_ 仅在用作限定符的第一个元素时具有此特殊含义;否则它是一个普通标识符。

示例

考虑以下程序

package b {
  class B
}

package a {
  package b {
    class A {
      val x = new _root_.b.B
    }
    class C {
      import _root_.b._
      def y = new B
    }
  }
}

这里,引用 _root_.b.B 指的是顶层包 b 中的类 B。如果省略了 _root_ 前缀,名称 b 将解析为包 a.b,并且,如果该包也不包含类 B,则会产生编译时错误。

程序

一个程序是一个顶层对象,它拥有一个名为main的成员方法,类型为(Array[String])Unit。程序可以从命令行执行。程序的命令参数作为类型为Array[String]的参数传递给main方法。

程序的main方法可以直接在对象中定义,也可以被继承。Scala 库定义了一个特殊的类scala.App,它的主体充当main方法。因此,继承自该类的对象m就是一个程序,它会执行对象m的初始化代码。

示例

以下示例将通过在模块test.HelloWorld中定义一个main方法来创建一个“Hello World”程序。

package test
object HelloWorld {
  def main(args: Array[String]) { println("Hello World") }
}

这个程序可以通过以下命令启动

scala test.HelloWorld

在 Java 环境中,以下命令

java test.HelloWorld

也可以正常工作。

HelloWorld也可以通过继承App而不是定义main方法来定义

package test
object HelloWorld extends App {
  println("Hello World")
}