Table of Contents

两种定义包的方式:

1 文件顶部定义

和 Java 一样,在文件顶部定义包名:

package org.shellcodes.lx

2 嵌套定义

package shellcodes {
  // 在 shellcodes 包里
  class Say(msg: String) {
    def say = println(msg)
  }

  // 在 shellcodes.lx 包里
  package lx {
    class lx {
     // 不用写 shellcodes.Say,由于 Say 是在顶层中,所以用 _root_。如果是位于同个顶层的其他子层中,也不用 shellcodes. 开头
      val say = new _root_.Say("hehe")
      say.say
    }
  }
}

3 导入包

import packageName.ClassName // 导入 ClassName
import packageName._ 或者 import packageName.{_} // 导入 packageName 所有成员
import packageName.ClassName._ // 导入 ClassName 所有成员,这个还可以用在函数体内
import packageName.{Class1, Class2} // 只导入 Class1 和 Class2
import packageName.{Class1 => NewClassName, Class2} // 导入 Class1 和 Class2,但 Class1 命名为 NewClassName
import packageName.{Class1 => _, _} // 导入除 Class1 以外的

Scala 默认导入了以下3个包:

import java.lang._
import scala._
import Predef._

4 访问修饰

private 不允许外部访问:

class Hello {
  class Hi {
    private val x: Int = 1
    class Hehe {
      println(x)
    }
  }
}

x 可以在更内层嵌套的 Hehe 访问,但不能在外层访问。

protected 只能在子类中访问。

public:没有标记为 private 和 protected 的都是 public 的,可以随意访问。

5 保护作用域

被 private[x] 或 protected[x] 修饰的表示在 x 这个范围内可见,x 可以是包、类或单例对象。比如:

class Hello {
  private[Hello] def sayHello {
    println("hello")
  }

  def hello {
    sayHello
  }
}

(new Hello).hello

Hello 类里的 sayHello 定义了 private[Hello],限定了 sayHello 只能够在 Hello 这个类中访问,所以是不能外部调用的,比如:

(new Hello).sayHello 会报错:

error: method sayHello in class Hello cannot be accessed in this.Hello。

保护作用域对大型项目非常有帮助。这个 Java 是做不到的。

protected[x] 表示允许 x 的子类、x 包和 x 类都可以访问。

6 private[this] 和 private 区别

private[this] 不允许同一类的其他对象访问,如:

class Hello {
  private val x = 1
  def hello(otherHello: Hello) {
    // 这里是可以访问同一类的其他对象 private 属性的
    println(otherHello.x)
  }
}

val hello1 = new Hello
val hello2 = new Hello
println(hello1.hello(hello2))

如果把 private val x = 1 改成 private[this] val x = 1,上面代码编译会报错:

error: value x is not a member of this.Hello

另外,伴生对象是可以访问 private 属性的,但 protected 属性没意义,因为伴生类没有子类:

class Hello {
  private val msg: String = "hello"
}

object Hello {
  def sayHello(hello: Hello) {
    println(hello.msg)
  }
}

val hello = new Hello
println(Hello.sayHello(hello))

7 包对象

可用来解决兼容性问题,比如早期的 List 是 scala.List,但新版本把它放到 scala.collection.immutable.List 里了,为了兼容老版本,引用了包对象。比如 Scala 的包对象定义大概定义如下:

package object scala {
  ...
  type List[+A] = scala.collection.immutable.List[A]
  val List = scala.collection.immutable.List
  ...
}

8 包示例

如果声明了 package,是不能直接用 scala 命令执行未编译的。

首先建立一个 hello 目录,里面有两个文件,hi.scala 和 xx.scala,文件内容如下:

// hi.scala
package hello

class Hello {
  println("hello")
}
// xx.scala
package xx

import hello.Hello

object Main extends App {
  new Hello
}

然后在 hello 同级目录下,执行 scalac hello/*.scala,最后会按类生成目录(这个是为了和 Java 一致)。

执行:

$ scala xx.Main