概述
Scala 代码存在于 Java 平台全局的包层次结构中。在 Scala 中有两种方式可以将代码放进命名的包中,第一种方法是在文件顶部添加 package
语句:
1package com.example.scala
2
3class MyClass {
4}
第二种方式是在文件顶部添加 package
语句,并使用花括号 {}
将代码括起来,称为打包(packaging):
1package com.example.scala {
2 class MyClass {
3 }
4}
使用第二种方法可以在一个文件中定义多个包的内容,例如:
1package com {
2 class A {}
3
4 package example {
5 class B {}
6
7 package scala {
8 class C {}
9 }
10 }
11}
在上面的示例中,我们在 com
包中定义了一个类 A
,在 com.example
包中定义了一个类 B
,在 com.example.scala
包中定义了一个类 C
。
显式导入
Scala 可以使用 import
子句显式导入包和它们的成员,被导入的类和对象可以用它们的简单名称访问,而不需使用限定名称。
1package com.example.scala
2
3abstract class Fruit(val name: String, val color: String)
4
5object Fruits {
6 object Apple extends Fruits("apple", "red")
7 object Orange extends Fruits("orange", "orange")
8 object Pear extends Fruits("pear", "yellowwish")
9 val menu = List(Apple, Orange, Pear)
10}
import
子句使得某个包或对象的成员可以只用它们的名字访问,而不需要在前面加上包名或对象名。
1// 导入 Fruits 抽象类
2import com.example.scala.Fruit
3
4// 导入 com.example.scala 包中的所有成员
5import com.example.scala._
6
7// 导入 Fruits 对象中的所有成员
8import com.example.scala.Fruits._
第一个对应 Java 的单类型导入,第二个对应 Java 的按需导入,区别在于Java使用星号 *
,而 Scala 使用下划线 _
(Scala 中星号 *
是一个合法的标识符)。第三个对应 Java 对类静态字段的导入。
Scala 的导入可以出现在任何地方,不仅仅是某个编译单元的最开始,它们还可以引用任意的值。
1def showFruit(fruit: Fruit) = {
2 // 导入一个普通对象的成员
3 import fruit._
4 println(name + "s are " + color)
5}
隐式导入
Scala 对每个程序都隐式地添加了一些导入,每个扩展名为 .scala
的源代码文件的顶部都隐式添加了如下三行导入语句:
1import java.lang._ // java.lang 包的全部成员
2import scala._ // scala 包的全部内容
3import Predef._ // Predef 对象的全部内容
-
java.lang
包包含了标准的 Java 类,由于该包是隐式导入的,因此可以直接使用Thread
,而不是java.lang.Thread
。 -
scala
包包含了 Scala 的标准类库,由于该包是隐式导入的,因此可以直接使用List
,而不是scala.List
。 -
Predef
对象包含了许多类型、方法和隐式转换的定义,由于该对象是隐式导入的,因此可以直接使用assert
,而不是Predef.assert
。
访问修饰符
包、类或对象的成员都可以添加 private
或 protected
这样的访问修饰符。
私有成员
标记为 private
的成员只在包含该定义的类或对象内部可见。在 Scala 中,这个规则同样适用于内部类。
1class Outer {
2 class Inner {
3 private def f() = { println("f") }
4 class InnerMost {
5 // Java 和 Scala 都允许
6 f()
7 }
8 }
9
10 // Java 允许,但 Scala 不允许
11 (new Inner()).f()
12}
受保护成员
跟 Java 相比,Scala 对 protected
成员的访问也更加严格,在 Scala 中,protected
成员只能从定义该成员的子类访问,而 Java 允许在同一个包内的其他类访问这个类的受保护成员。
1package p {
2 class Super {
3 protected def f() = { println("f") }
4 }
5
6 class Sub extends Super {
7 f()
8 }
9
10 class Other {
11 // error
12 (new Super).f()
13 }
14}
公有成员
-
Scala 并没有专门的修饰符用来标记公有成员,任何没有被标记为
private
或protected
的成员都是公共的。 -
公共成员可以从任意位置访问到。
保护的范围
可以用限定词对 Scala 中的访问修饰符机制进行增强,形如 private[X]
或 protected[X]
的修饰符的含义是对此成员的访问限制上至 X
都是私有或受保护的,其中 X
表示某个包含该定义的包、类或单例对象。
可见性和伴生对象
-
Java 中,静态成员和实例成员同属一个类,因此访问修饰符对它们的应用方式是统一的。
-
Scala 的类没有静态成员,而是用伴生对象来承载哪些只存在一次的成员。
Package 对象
添加到 package 里的代码有类、特质和孤立对象。
如果你希望定义在整个 package 中都能使用的助手方法,可以将它放在 package 的顶层,具体的做法是把定义放在 package 对象中。每个 package 都有一个 package 对象,任何被放在 package 对象里的定义都会被当作这个package 本身的成员。