1.将代码放进包里:在Scala中,可以通过两种方式将代码放进带名字的包里。第一种是在文件顶部放置一个package子句,让整个文件的内容放进指定的包:
package bobsrockets.navigation
class Navigator
另一种将Scala代码放进包的方式更像是C#的命名空间。可以在package子句之后加上一段用花括号包起来的代码块,这个代码块包含了进入该包的定义。这个语法称为打包:
package bobsrockets.navigation{
class Navigator
}
效果跟上面一样。这样更通用的表示法可以让我们在一个文件里包含多个包的内容。如,可以把某个类的测试代码跟原始代码放在同一个文件里,不过分成不同的包:
package bobsrockets{
package navigation{
//位于bobsrocket.navigation包中
class Navigator
package tests{
//位于bobsrocket.navigation.tests包中
class NavigatorSuite
}
}
}
2.对相关代码的精简访问:可以访问同一个包里的其它成员:
package navigation{
class Navigator{
//不需要navigation.StarMap
val map=new StarMap
}
class Ship
}
每个你能编写的顶层包都被当作是_root_包的成员。
3.引入:在Scala中,我们可以用import子句引入包和它们的成员:
//到Fruit的便捷访问
import bobsdelights.Fruit
//到bobsdelights所有成员的便捷访问
import bobsdelights._
//到Fruit所有成员的便捷访问
import bobsdelights.Fruit._
Scala的引入可以出现在任何地方,不仅仅是在某个编译单元的最开始,它们还可以引用任意的值:
abstract class Fruit(val name:String,val color:String)
def showFruit(fruit:Fruit){
import fruit._
println(name+"s are"+color)
}
在Scala中,引入可以:
- 出现在任意位置
- 引用对象(不论是单例还是常规对象),而不只是包
- 让你重命名并隐藏某些被引入的成员
object Fruits{
object Apple extends Fruit("apple","red")
object Orange extends Fruit("orange","orange")
object Pear extends Fruit("pear","yellowish")
}
import Fruits.{Apple,Orange}
这只会从Fruits对象引入Apple和Orange两个成员。
import Fruits.{Apple=>McIntosh,Orange}
引入Orange和Apple,并将Apple改名为McIntosh。重命名后可以用Fruits.Apple或McIntosh来访问成员。重命名子句形式是“<原名> => <原名>”。
import Fruits.{_}
这将引入Fruits所有成员,跟import Fruiits._的含义是一样的。
import Fruits.{Apple=>McIntosh,_}
引入Fruits所有成员,但会把Apple重命名为McIntosh。
import Fruits.{Pear=>_,_}
引入除Pear之外的所有成员。形如“<原名> => _”的子句将在引入的名称中排除<原名>。从某种意义上讲,将某个名称重命名为“_”意味着将它完全隐藏掉。总之,引入选择器可以包含:
- 一个简单的名称x。这将把x包含在引入的名称集里。
- 一个重命名子句x=>y。这会让名为x的成员以y的名称可见。
- 一个隐藏子句x=>_。这会从引入的名称集里排除掉x。
- 一个捕获所有的“_”。这会引入除了之前子句中提到的成员外的所有成员。如果要给出捕获所有子句,它必须出现在引入选择列表的末尾。
4.隐式引入:Scala对每个程序都隐式地添加了一些引入。本质上,这就好比每个扩展名为“.scala”的源码文件的顶部都添加了如下三行引入子句:
import java.lang._ //java.lang包的全部内容
import scala._ //scala包的全部内容
import Predef._ //Predef对象的全部内容
由于scala是隐式引入的,举例来说,可以直接写List,而不是scala.List。
Predef对象包含了许多类型、方法和隐式转换的定义,这些定义在Scala程序中经常被用到。
Scala对这三个引入子句做了一些特殊处理,后引入的会遮挡前面的。举例来说,scala包和Java1.5版本后的java.lang包都定义了StringBuilder类。由于scala的引入遮挡了java.lang的引入,因此StringBuilder这个简单名称会引用到scala.StringBuilder,而不是java.lang.StringBuilder。
5.访问修饰符:Scala对访问修饰符的处理大体上跟Java保持一致,不过有些区别。
私有成员:Scala对私有成员的处理跟Java类似。标为private的成员只在包含该定义的类或对象内部可见。在Scala中,这个规则同样适用于内部类。但Java可以从外部类访问其内部类的私有成员。
受保护成员:跟Java相比,Scala对protected成员的访问也更严格。在Scala中,protected的成员只能从定义该成员的子类访问。而Java允许同一个包内的其他类访问这个类的受保护成员。Scala提供了另一种方式来达到这个效果,因此protected不需要为此放宽限制。
公共成员:Scala并没有专门的修饰符用来标记公共成员:任何没有被标为private或protected的成员都是公共的。公共成员可以从任何位置访问到。
保护的范围:我们可以用限定词对Scala中的访问修饰符机制进行增强。形如private[X]或protected[X]的修饰符的含义是对此成员的访问限制“上至”X都是私有或受保护的,其中X表示某个包含该定义的包、类或单例对象。我们可以用protected[X],那么X表示的包、类或对象中,都能访问这个被标记的定义。如果X是包名,那就跟Java的protected一样了。
Scala还提供了比private限制范围更小的访问修饰符。被标记为private[this]的定义,只能在包含该定义的同一个对象中访问。这样的定义被称作是对象私有的。
可见性和伴生对象:一个类会将它的所有访问权跟它的伴生对象共享,反过来也一样。
6.包对象:如果你有某个希望在整个包都能用的助手方法,大可将它放在包 顶层具体的做法是把定义放在包对象当中。每个包都允许有一个包对象,任何被放在包对象里的定义都会被当作这个包本身的成员:
//位于文件bobsdelights/package.scala中
package object bobsdelights {
def showFruit(fruit:Fruit)={
import fruit._
println(name+"s are"+color)
}
}
和打包的区别是包对象包含了一个object关键字。这是一个包对象,而不是一个包。包对象经常用于包级别的类型别名和隐式转换。顶层的scala包也有一个包对象,其中的定义对所有Scala代码都可用。
包对象会被编译成名为package.class的类文件,该文件位于它增强的包的对应目录下。源文件最好能保持相同的习惯,也就是说我们一般会将包对象bobsdelights的源码放在bobsdelights目录下名为package.scala的文件当中。