《快学Scala》第七章——包和引入 学习笔记

本章要点概览:

  • 包也可以像内部类那样嵌套。
  • 包路径不是绝对路径。
  • 包声明链x.y.z并不自动将中间包x和x.y变成可见。
  • 位于文件顶部不带花括号的包声明在整个文件范围内有效。
  • 包对象可以持有函数和变量。
  • 引入语句可以引入包、类和对象。
  • 引入语句可以出现在任何位置。
  • 引入语句可以重命名和隐藏特定成员。
  • java.lang、scala和Predef总是被引入。

7.1 包

Scala的包和Java中的包或者C++中的命名空间的目的是相同的:管理大型程序中的名称。例如:Map这个名称可以同时出现在scala.collection.immutable和scala.collection.mutable包中而不会发生冲突。要访问它们中的任何一个,可以使用完全限定的名称scala.collection.imutable.Map或scala.collection.mutable.Map,也可以使用引入语句提供一个更短小的别名。
要增加条目到包中,可以将其包含在包语句中,比如:
package com{
    package horstman{
        package impatient{
            class Employee
            ...
        }
    }
}
这样类名Employee就可以在任意位置以com.horstmann.impatient.Employee访问到了。
与对象或类的定义不同,同一个包可以定义在多个文件当中。
说明:源文件的目录和包之间并没有强制的关联关系。

7.2 作用域规则

在Scala中,包的作用域比起Java来更加前后一致。Scala的包和其他作用域一样地支持嵌套。可以访问上层作用域中的名称。
Scala中有一个特性,那就是scala包总是被引入。因此包中没有其他collection包,引入collection包中的mutable.ArrayBuffer时可以使用相对路径collection.mutable.ArrayBuffer而不出错,但是当其他包中有collection这个包,如com.horstmann.collection,再使用相对路径collection.mutable.ArrayBuffer就可能因编译器找不到对应的包而引发错误。在Java中,这个问题不会发生,因为包名总是绝对的,从包层级的最顶端开始。但是在Scala中,包名是相对的,就像内部类的名称一样。内部类通常不会遇到这个问题,因为所有代码都在同一个文件当中,由负责该文件的人直接控制。但是包不一样了,任何人都可以在任何时候向任何包添加内容。
解决方法之一是使用绝对包名,以_root_开始,例如:
val subordinates = new _root_.scala.collection.mutable.ArrayBuffer[Employee]
另一种做法是使用“串联式”包语句。
说明:大多数程序员都是用完整的包名,知识不加_root_前缀。只要避免用scala、java、com、org等名称来命名嵌套的包,这样做就是安全的。


7.3 串联式包语句

包语句可以包含一个“串”,或者说路径区段,例如:scala.collection.mutable包中的ArrayBuffer,这样的包语句限定了可见的成员。

7.4 文件顶部标记法

除了嵌套标记法外,也可以在文件顶部使用package语句,不带或括号。例如:
package com.horstmann.impatient
package people

class Person
...

//上面的代码等价于
package com.horstmann.impatient{
    package people{
        class Person
        ...
        //直到文件末尾
    }
}

注意在上面的示例中,文件的所有内容都属于com.horstmann.impatient.people,但com.horstmann.impatient包中的内容是可见的,可以被直接引用。

7.5 包对象

包可以包含类、对象和特质,但不能包含函数或变量的定义,这是Java虚拟机的局限。把工具函数或常量添加到包而不是某个Utils对象,这是更加合理的做法。包对象的出现正是为了解决这个局限。
每个包都可以有一个包对象。需要再父包中定义它,且名称与子包一样。例如:
package com.horstmann.impatient

package object people{
    val defaultName = "John Q, Public"
}

package people {
    class Person{
        var name = defaultName  //此包对象拿到的常量
    }
}
注意defaultName不需要加限定词(父包的路径),因为它位于同一个包内。在其他地方,这个常量可以使用com.horstmann.impatient.people.defaultName访问到。
在幕后,包对象被编译成带有静态方法和字段的JVM类,名为package.class,位于相应的包下。对应到本例中,就是com.horstmann.impatient.people.package,其中有一个静态字段defaultName。(在JVM中,可以使用package作为类名。)

7.6 包可见性

在Java中,没有被声明为public、private或protected的类成员在包含该类的包中可见。在Scala中,可以通过修饰符达到同样的效果。以下方法在它自己的包中可见:
package com.horstmann.impatient.people
class Person{
    private[people] def description = "A person with name " + name 
    ...
}
可以将可见度延展到上层包:
private[impatient] def description = "A person with name " + name

7.7 引用

引用语句可以使用更短的名称而不是原来较长的名称。如:import java.awt.Color 这样就可以在代码中写Color而不是java.awt.Color了,同时还可以通过Color.类名 的方式调用Color类中的方法实现想要的功能。
在Scala中,可以通过_引用某个包的全部成员:import java.awt._  这和Java中的通配符*一样。(在Scala中,*是合法的表示符。在上面的例子中完全可以定义com.horstmann.*.people这样的包,但建议还是不要这样做。)
在Scala中,也可以通过_引入类或对象的所有成员。一旦引入了某个包,就可以用较短的名称访问其子包。例如:
import java.awt._

def handler(evt: event.ActionEvent){//java.awt.event.ActionEvent
    ...
}

其中,event包时java.awt包的成员,因此引用语句把它带进了作用域。

7.8 任何地方都可以声明引入

在Scala中,import语句可以出现在任何地方,并不仅限于文件顶部。import语句的效果一直延伸到包含该语句的块末尾。例如:
class Manager {
    import scala.collection.mutable._
    val subordinates = new ArrayBuffer[Employee]
    ...
}
通过将引入放置在需要这些引入的地方,可以大幅减少可能的名称冲突。

7.9 重命名和隐藏方法

如果想要引入包中的几个成员,可以像下面这样使用选取器(selector):
imprt java.awt.{Color, Font}
选取器语法(将用到的包中的某几个类通过花括号{}括起来)还允许重命名选到的成员:
import java.util.{HashMap => JavaHashMap}
import scala.collection.mutable._
这样,JavaHashMap就是java.util.HashMap,而HashMap则对应scala.collection.mutable.HashMap。
选取器HashMap => _将隐藏某个成员而不是重命名它。这仅在需要引入其他成员时有用:
import java.util.{HashMap => _, _}
import scala.collection.mutable._
现在,HashMap无 二义地指向scala.collection.mutable.HashMap,因为java.util.HashMap被隐藏起来了。


7.10 隐式引入

每个Scala程序都隐式地(默认)以如下代码开始(即默认自动导入以下包,这些包下面的子包不需要再带上这些前缀):
import java.lang._
import scala._
import Predef._
和Java程序一样,java.lang总是被引入。接下来,scala包也被引入,不过方式有些特殊。不像所有其他引入,这个引入被允许可以覆盖之前的而引入。例如,scala.StringBuilder会覆盖java.lang.StringBuffer而不是与之冲突。最后,Predef对象被引入。它包含了相当多有用的函数。(这些同样可以被放置在scala包对象中,不过Predef在Scala还没有加入包对象之前就已经存在了。)

由于scala包默认被引入,对于那些以scala开头的包,完全不需要写全这个前缀。例如:collection.mutable.HashMap和以下这个写法scala.collection.mutable.HashMap效果一样。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

love666666shen

谢谢您的鼓励!

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值