Scala學習(七)---包和引入


包和引入

摘要:

在本篇中,你將會了解到Scala中的包和引入語句是如何工作的。相比Java不論是還是引入都更加符合常規,也更靈活一些。本篇的要點包括:

1. 包也可以像內部類那樣嵌套

2. 包路徑不是絕對路徑

3. 包聲明鏈x.y.z並不自動將中間包x和x.y變成可見

4. 位於文件頂部不帶花括號的包聲明在整個文件范圍內有效

5. 包對象可以持有函數和變量

6. 引入語句可以引入包、類和對象

7. 引入語句可以出現在任何位置

8. 引入語句可以重命名和隱藏特定成員

9. java.lang、scala和Predef總是被引入

Scala中包含義

Scala的包Java中的包或者C++中的命名空間的目的是相同的:管理大型程序中的名稱。舉例來說,Map這個名稱可以同時出現在scala.collection.immutablescala. collection.mutable而不會沖突。要訪問它們中的任何一個,你可以使用完全限定的名稱scala.collection.immutable.Map或scala.collection.mutable.Map,也可以使用引入語句來提供一個更短小的別名

要增加條目到包中,你可以將其包含在包語句當中,比如:

package com {

package horstmann {

package impatient {

class Employee

…….

}

}

}

這樣一來類名Employee就可以在任意位置以com.horstmann.impatient.Employee訪問到了

Scala中包定義

與對象或類的定義不同,同一個包可以定義在多個文件當中。前面這段代碼可能出現在文件Employee.scala中,而另一個名為Manager.scala的文件可能會包含:

package com {

package horstmann {

package impatient {

class Manager

…….

}

}

}

這說明了,源文件的目錄和包之間並沒有強制的關聯關系。你不需要將Employee.scala和Manager.scala放在com/horstmann/impatient目錄當中

換個角度講,你也可以在同一個文件當中為多個包貢獻內容。Employee.scala文件可以包含:

package com {

package horstmann {

package impatient {

class Employee

…….

}

}

}

package org {

package bigjava {

class Counter

…….

}

}

作用域規則

包嵌套

在Scala中,包的作用域比起java來更加前后一致。Scala的包和其他作用域一樣地支持嵌套,你可以訪問上層作用域中的名稱。例如:

package com {

package horstmann {

object Utils {

def percent of (value: Double, rate: Double) = value*rate/100

……….

}

package impatient {

class Employee

…….

def giveRaise( rate : Scala.Double) {

salary += Utils.percentOf( salary,rate )

}

}

}

}

注意Utils.percentOf修飾符。Utils類定義於父包,所有父包中的內容都在作用域內,因此沒必要使用com.horstmann.Utils.precentOf。

包沖突

不過,這里有一個瑕疵。假定有如下代碼:

package com {

package horstmann {

package impatient {

class Manager {

val suboardinates = new collection.mutable.ArrayBuffer[Employee]

…….

}

}

}

}

這里我們利用到一個特性,那就是scala包總是被引入。因此,collection包實際上指向的是scala.collection。現在假定有人加入了如下的包,可能位於另一個文件當中:

package com {

package horstmann {

package collection {

…….

}

}

}

這下Manager類將不再能通過編譯。編譯器嘗試在com.horstmann.collection包中查找mutable成員未果。Manager類的本意是要使用頂級的scala包中的collection包,而不是隨便什么存在於可訪問作用域中的子包

包沖突方案

在Java中,這個問題不會發生,因為包名總是絕對的,從包層級的最頂端開始。但是在Scala中,包名是相對的,就像內部類的名稱一樣。內部類通常不會遇到這個問題,因為所有代碼都在同一個文件當中,由負責該文件的人直接控制。但是包不一樣,任何人都可以在任何時候向任何包添加內容。

解決方法之一是使用絕對包名,以_root_開始,例如:

val subordinates=new _root_.scala.collction.mutable.ArrayButfer[Employee]

另一種做法是使用"串聯式"包語句,在后面會詳細講到

串聯式包語句

包語句可以包含一個"串",或者說路徑區段,例如:

package com.horstmann.impatient { // com和com.horstmann的成員在這里不可見

package people {

class Person

}

}

這樣的包語句限定了可見的成員。現在com.horstmann.collection包不再能夠以collection訪問到了

文件頂部標記法

除了我們到目前為止看到的嵌套標記法外,你也可以在文件頂部使用package語句,不帶花括號。例如:

package com.horstmann.impatient

package people

class Person

這等同於

package com.horstmann.impatient {

package people {

class Person

……

// 直到文件末尾

}

}

如果文件中的所有代碼屬於同一個包的話:這也是通常的情形,這是更好的做法。還需注意是:在上面的示例當中,文件的所有內容都屬於com.horstmann.impatient.people,但com.horstmann.impatient包的內容是可見的,可以被直接引用

包對象

包可以包含對象特質,但不能包含函數或變量的定義。很不幸,這是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作為類名。對源文件使用相同的命名規則是好習慣,可以把包對象放到文件com/horstmann/impatient/people/package.scala。這樣一來,任何人想要對包增加函數或變量的話,都可以以很容易地找到對應的包對象

包可見性

在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

引入

引入語句讓你可以使用更短的名稱而不是原來較長的名稱。寫法如下:

import java.awt.Color

這樣一來,你就可以在代碼中寫Color而不是java.awt.Color了,這就是引入語句的唯一目的。如果你不介意長名稱,你完全不需要使用引入。你也可以引入某個包的全部成員:

import java.awt._

這和Java中的通配符*一樣。在Scala中,*是合法的標識符。你完全可以定義com.horstmann.*.people這樣的包,但請別這樣做。你還可以引入類對象的所有成員:

import java.awt.Color._

val c1 =RED // Color.RED

val c2=decode("#ff0000") // Color.decode

這就象Java中的import static。Java程序員似乎挺害怕這種寫法,但在Scala中這樣的弓很常見。一旦你引入了某個包,你就可以用較短的名稱訪問其子包。例如:

import java.awt._

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

……….

}

event包是java.awt包的成員,因此引入語句把它也帶進了作用域

任何地方都可以聲明引入

在Scala中,import語句可以出現在任何地方,並不僅限於文件頂部。import語句的效果一直延伸到包含該語句的塊末尾。例如:

class Manager {

import scala.collection.mutable._

val subordinates = new ArrayBuffer[Employee]

}

這是個很有用的特性,尤其是對於通配引入而言。從多個源引入大量名稱總是讓人擔心。事實上,有些Java程序員特別不喜歡通配引入,以至於從不使用這個特性,而是讓IDE幫他們生成一長串引入語句。通過將引入放置在需要這些引入的地方,你可以大幅減少可能的名稱沖突

重命名和隱藏方法

重命名

如果你想要引人包中的幾個成員,可以像這樣使用選取器( selector):

import java.awt.Color.{ Color,Font }

選取器語法還允許你重命名選到的成員:

import java.util.{ HashMap=>JavaHashMap }

import scala.collection.mutable._

這樣一來,JavaHashMap就是java.utiI.HashMap,而HashMap則對應scala.collection.mutable.HashMap。

隱藏方法

選取器HashMap =>_隱藏某個成員而不是重命名它。這僅在你需要引入其他成員時有用:

import java.util.{HashMap=>_,_ }

import scala.collection.mutable._

現在,HashMap無二義地指向scala.collection.mutable.HashMap,因為java.util.HashMap被隱藏起來了

隱式引入

每個Scala程序都隱式地以如下代碼開始:

import java.lang._

import scala._

import Predef._

和Java程序一樣,java.lang總是被引入。接下來,scala包也被引入,不過方式有些特殊。不像所有其他引入,這個引入被允許可以覆蓋之前的引入。舉例來說,scala.StringBuilder覆蓋java.lang.StringBuilder而不是與之沖突。最后,Predef對象被引入。它包含了相當多有用的函數,這些同樣可以被放置在scala包對象中,不過Predef在Scala還沒有加入包對象之前就存在了。

由於scala包默認被引入,對於那些以scala開頭的包,你完全不需要寫全這個前綴。例如:

collection.mutable.HashMap

上述代碼和以下寫法一樣好:

scala.collection.mutable. HashMap

如果,您認為閱讀這篇博客讓您有些收獲,不妨點擊一下右下角的【推薦】。
如果,您希望更容易地發現我的新博客,不妨點擊一下左下角的【關注我】。
如果,您對我的博客所講述的內容有興趣,請繼續關注我的后續博客,我是【Sunddenly】。

本文版權歸作者和博客園共有,歡迎轉載,但未經作者同意必須保留此段聲明,且在文章頁面明顯位置給出原文連接,否則保留追究法律責任的權利。


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM