Scala 代码组织

概述

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

访问修饰符

包、类或对象的成员都可以添加 privateprotected 这样的访问修饰符。

私有成员

标记为 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 并没有专门的修饰符用来标记公有成员,任何没有被标记为 privateprotected 的成员都是公共的。

  • 公共成员可以从任意位置访问到。

保护的范围

可以用限定词对 Scala 中的访问修饰符机制进行增强,形如 private[X]protected[X] 的修饰符的含义是对此成员的访问限制上至 X 都是私有或受保护的,其中 X 表示某个包含该定义的包、类或单例对象。

可见性和伴生对象

  • Java 中,静态成员和实例成员同属一个类,因此访问修饰符对它们的应用方式是统一的。

  • Scala 的类没有静态成员,而是用伴生对象来承载哪些只存在一次的成员。

Package 对象

添加到 package 里的代码有类、特质和孤立对象。

如果你希望定义在整个 package 中都能使用的助手方法,可以将它放在 package 的顶层,具体的做法是把定义放在 package 对象中。每个 package 都有一个 package 对象,任何被放在 package 对象里的定义都会被当作这个package 本身的成员。

上一页
下一页