包
使用包的目的:管理大型程序中的名称。
包可以嵌套。同一个包可以定义在多个文件当中,也不需要将源文件的目录和包之间对应起来。
1
2
3
4
5
6
7
8
|
package
com
{
package
horstmann
{
package
impatient
{
class
Employee
.
.
.
}
}
}
|
java.lang、scala和Predef包总是被引入的。
作用域规则
可访问上层作用域中的名称。
在Scala中包名是相对的。而在Java中包名是绝对的,是从包层级的最顶端开始的。相对的包名可能会带来问题。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
|
package
com
{
package
horstmann
{
package
impatient
{
class
Manager
{
// 因为scala包总是被引入,所以下面的collection包实际是指scala.collection
val
subordinates
=
new
collection
.
mutable
.
ArrayBuffer
[
Employee
]
.
.
.
}
}
}
}
// 这时如果再有一个包
package
com
{
package
horstmann
{
package
collection
{
.
.
.
}
}
}
|
在上面的情况下,由于编译器会尝试从com.horstmann.collection中查找mutable,但实际是找不到的,会使得编译Manager类无法通过。
解决方式之一,是使用绝对包名——以_root_开始:
1
|
val
subordinates
=
new
_root_
.
scala
.
collection
.
mutable
.
ArrayBuffer
[
Employee
]
|
另一种方法是使用“串联式包语句”。
串联式包语句
1
2
3
4
5
6
7
|
package
com
.
horstmann
.
impatient
{
// com和com.horstmann的成员在这里是不可见的
package
people
{
class
Person
.
.
.
}
}
|
使用了串联式包语句,由于包的上级层次不可见了,所以也就没有了上面提到的问题了。
文件顶部标记
如果文件中的所有代码都属于同一个包,在文件顶部标记包是更好的做法。
1
2
3
4
5
6
7
8
9
10
11
12
|
package
com
.
horstmann
.
impatient
package
people
class
.
.
.
// 上面的等同于
package
com
.
horstmann
.
impatient
{
package
people
{
class
Person
.
.
.
}
}
|
文件的内容都在com.horstmann.impatient.people包内,但com.horstmann.impatient包内的内容对于这个文件来说是可见的(com和com.horstmann是不可见的)。
包对象
包可以包含类、对象和特质,但不能包含函数或变量的定义,这是JVM的局限。将工具函数或常量添加到包,而不是某个Utils对象,更为合理。(这也是初学时觉得Scala的包引入有些奇怪的原因了。)包对象就是为了解决这个局限。
每个包可以有一个包对象,需要在父包中定义,名称与子包一样。
1
2
3
4
5
6
7
8
9
10
11
|
package
com
.
horstmann
.
impatient
package
object
people
{
val
defaultName
=
"John Q. Public"
}
package
people
{
class
Person
{
val
name
=
defaultName
}
}
|
在编译实现时,包对象被编译成带有静态方法和字段的JVM类,名为package.class,位于相应的包下。
对源文件使用相同的命名规则是好习惯。将包对象放在com/horstmann/impatient/people/package.scala中,会方便其他人找到。
包可见性
Java内,没有修饰符的类成员默认是包内可见的;而Scala则是“公共可见”的。如果要想让成员在某个包内可见,可以使用类似于对象私有字段的语法来指定: private[package-name]。
1
2
3
4
5
6
|
package
com
.
horstmann
.
impatient
.
people
class
Person
{
private
[
impatient
]
def
description
=
"A person with name "
+
name
.
.
.
}
|
引入
1
2
3
4
5
6
7
|
import
java
.
awt
.
Color
// 引入Color
import
java
.
awt
.
_
// 引入包内所有成员
def
handler
(
evt
:
event
.
ActionEvent
)
{
// java.awt.event.ActionEvent
.
.
.
// 因为引入了java.awt,所以可以省去前面的部分
}
|
import语句可以出现在任何地方,而不是只能在文件顶部。import的效果从开始延伸到语句块的结束。这可以大幅减少名称冲突的可能性。
重命名和隐藏成员
如果想要引入包中的几个成员,可以使用selector(选取器):
1
2
3
4
5
6
7
|
import
java
.
awt
.
{
Color
,
Font
}
// 重命名成员
import
java
.
util
.
{
HashMap
=
>
JavaHashMap
}
// 隐藏成员
import
java
.
util
.
{
HashMap
=
>
_
,
_
}
// 引入了util包的所有成员,但是HashMap被隐藏了
|
隐式引入
如上所说,Scala总会引入 java.lang._ 、 scala._ 和 Predef._
java.lang总会被引入,然后引入scala包,scala包将会覆盖java.lang中的内容,最后引入Predef包。这里也能解释,为什么以scala开头的包,在使用时都是省去scala.的。
http://nerd-is.in/2013-08/scala-learning-packages-and-imports/