标识符、名称和作用域

Scala 中的名称标识类型、值、方法和类,这些统称为实体。名称由局部定义继承导入语句包语句引入,这些统称为绑定

不同类型的绑定具有定义的优先级。

  1. 在与引用它们的编译单元相同的编译单元中定义的局部、继承或由包语句提供的定义具有最高优先级。
  2. 显式导入具有次高优先级。
  3. 通配符导入具有次次高优先级。
  4. 由包语句提供的定义,但不在与引用它们的编译单元相同的编译单元中定义,以及由编译器提供但未在源代码中显式编写的导入,具有最低优先级。

存在两个不同的命名空间,一个用于类型,另一个用于。同一个名称可能表示类型和项,具体取决于使用该名称的上下文。

绑定具有作用域,在该作用域中,可以使用简单名称访问由单个名称定义的实体。作用域是嵌套的。某个内部作用域中的绑定会遮蔽同一作用域中优先级较低的绑定,以及外部作用域中优先级相同或较低的绑定。

请注意,遮蔽只是偏序关系。在以下示例中,x 的两个绑定都不会遮蔽对方。因此,块中最后一行对 x 的引用是模棱两可的。

val x = 1
locally {
  import p.X.x
  x
}

对未限定的(类型或项)标识符 ´x´ 的引用由唯一的绑定绑定,该绑定

如果不存在这样的绑定,则会发生错误。如果“x”由导入子句绑定,则简单名称“x”被视为等同于导入子句将“x”映射到的限定名称。如果“x”由定义绑定,则“x”引用由该绑定引入的实体。在这种情况下,“x”的类型是引用实体的类型。

对限定(类型或术语)标识符“e.x”的引用是指类型“T”的成员,该成员是“e”的成员,并且在与标识符相同的命名空间中具有名称“x”。如果“T”不是值类型,则会发生错误。“e.x”的类型指定为类型指示符

绑定优先级意味着源代码在文件中的捆绑方式会影响名称解析。特别是,导入的名称优先级高于其他文件中定义的名称,这些名称可能因为在当前包或封闭包中定义而可见。

请注意,包定义的优先级最低,因为包是开放的,可以在任意编译单元中定义。

package util {
  import scala.util
  class Random
  object Test extends App {
    println(new util.Random)  // scala.util.Random
  }
}

编译器在每个源文件的序言中提供导入。从概念上讲,此序言具有以下形式,其中大括号表示嵌套范围

import java.lang.*
{
  import scala.*
  {
    import Predef.*
    { /* source */ }
  }
}

这些导入的优先级最低,因此它们始终会被用户代码覆盖,用户代码可能包含竞争的导入和定义。它们还会增加嵌套深度,如所示,因此后面的导入会覆盖前面的导入。

为了方便起见,允许将类型标识符的多个绑定绑定到相同的底层类型。当导入子句引入具有相同绑定优先级的成员类型别名的绑定时,这通常通过通配符导入实现。这允许导入冗余类型别名,而不会引入歧义。

object X { type T = annotation.tailrec }
object Y { type T = annotation.tailrec }
object Z {
  import X.*, Y.*, annotation.tailrec as T  // OK, all T mean tailrec
  @T def f: Int = { f ; 42 }                // error, f is not tail recursive
}

类似地,允许导入由包语句引入的名称的别名,即使这些名称严格来说是模棱两可的

// c.scala
package p { class C }

// xy.scala
import p.*
package p { class X extends C }
package q { class Y extends C }

X定义中的C的引用严格来说是模棱两可的,因为C可以通过不同文件中的包子句获得,并且不能覆盖导入的名称。但由于引用相同,因此该定义被视为覆盖了导入。

示例

假设在不同的编译单元中,包pq中分别定义了名为X的两个对象。

package p {
  object X { val x = 1; val y = 2 }
}

package q {
  object X { val x = true; val y = false }
}

以下程序说明了不同类型的绑定及其之间的优先级。

package p {                     // `X' bound by package clause
  import Console.*              // `println' bound by wildcard import
  object Y {
    println(s"L4: $X")          // `X' refers to `p.X' here
    locally {
      import q.*                // `X' bound by wildcard import
      println(s"L7: $X")        // `X' refers to `q.X' here
      import X.*                // `x' and `y' bound by wildcard import
      println(s"L9: $x")        // `x' refers to `q.X.x' here
      locally {
        val x = 3               // `x' bound by local definition
        println(s"L12: $x")     // `x' refers to constant `3' here
        locally {
          import q.X.*          // `x' and `y' bound by wildcard import
//        println(s"L15: $x")   // reference to `x' is ambiguous here
          import X.y            // `y' bound by explicit import
          println(s"L17: $y")   // `y' refers to `q.X.y' here
          locally {
            val x = "abc"       // `x' bound by local definition
            import p.X.*        // `x' and `y' bound by wildcard import
//          println(s"L21: $y") // reference to `y' is ambiguous here
            println(s"L22: $x") // `x' refers to string "abc" here
          }
        }
      }
    }
  }
}