Scala入門到精通——第八節 包和引入
本節主要內容
- 包的作用與定義
- 包的作用域與引入(import)的使用方法
- 訪問控制
- 包物件
- import高階特性
- 內部類
包的作用與定義
同java中的包、C++中的名稱空間一樣,Scala中的包主要用於大型工程程式碼的組織同時也解決命名衝突的問題。Scala中的包與java有著諸多的相似之處,但Scala語言中的包更加靈活。
//將程式碼組織到cn.scala.xtwy包中
package cn.scala.xtwy
abstract class Animal {
//抽象欄位(域)
var height:Int
//抽象方法
def eat:Unit
}
class Person(var height:Int) extends Animal{
override def eat()={
println("eat by mouth")
}
}
object Person extends App{
new Person(10).eat()
}
上述包也可通過下面的包定義方式進行:
//下面的程式碼定義了一個cn.scala.xtwy包
//在程式的任何地方都可以通過cn.scala.xtwy.Teacher來使用Teacher這個類
package cn{
package scala{
package xtwy{
class Teacher {
}
}
}
}
將上面的程式碼命名為Teacher.scala,程式碼組織如下:
可以看到我們將程式碼放在預設包下面,但編碼後的位元組碼檔案被自動組織到cn.scala.xtwy資料夾當中,如下圖
可以看出,我們可以在任何地方進行包中類的定義,scala幫助我們進行自動檔案組織
我們將Teacher.scala內容修改如下:
package cn{
package scala{
package xtwy{
class Teacher {
}
}
}
}
//添加了cn.scala.spark包,包中定義了一個SparkDemo類
package cn{
package scala{
package spark{
class SparkDemo{
}
}
}
}
下圖給出的是Teacher.scala編譯後產生的包檔案:
通過前面的介紹,我們知道了如何定義包,包是怎麼組織程式碼的。在實際開發過程當中,儘量使用java包的定義方式並將程式碼集中進行管理,這樣別人讀程式碼的時候更方便,程式碼更簡潔。
包的作用域與引入(import)的使用方法
下面的程式碼給出了包的作用域和引入的使用方法
package cn{
package scala{
//在包cn.scala下建立了一個Utils單例
object Utils{
def toString(x:String){
println(x)
}
//外層包無法直接訪問內層包,下面這一行程式碼編譯通不過
//def getTeacher():Teacher=new Teacher("john")
//如果一定要使用的話,可以引入包
import cn.scala.xtwy._
def getTeacher():Teacher=new Teacher("john")
}
//定義了cn.scala.xtwy
package xtwy{
class Teacher(var name:String) {
//演示包的訪問規則
//內層包可以訪問外層包中定義的類或物件,無需引入
def printName()={Utils.toString(name)}
}
}
}
}
object appDemo{
//scala允許在任何地方進行包的引入,_的意思是引入該包下的所有類和物件
import cn.scala._
import cn.scala.xtwy._
def main(args: Array[String]): Unit = {
Utils.toString(new Teacher("john").name)
new Teacher("john").printName()
}
}
訪問控制
在java語言中,主要通過public、private、protected及預設控制來實現包中類成員的訪問控制,當定義一個類時,如果類成員不加任何訪問控制符時,表示該類成員在定義該類的包中可見。在scala中沒有public關鍵字,僅有private 和 protected訪問控制符,當一個類成員不加private和protected時,它的訪問許可權就是public。下面逐個進行講解:
1 private 成員
private成員同java是一樣的,所有帶該關鍵字修飾的成員僅能在定義它的類或物件中使用,在外部是不可見的
class Student(var name:String,var age:Int){
private var sex:Int=0
//內部類
class Course(val cName:String,val gpa:Float){
//可以直接訪問其外部類的私有成員
def getStudentSex(student:Student)= student.sex
}
}
//班級類
class Class{
//下面這條語句統計通不過,因為sex是私有的
// def getStudentSex(student:Student)=student.sex
}
object Student {
private var studentNo:Int=0;
def uniqueStudentNo()={
studentNo+=1
studentNo
}
def apply(name:String,age:Int)=new Student(name,age)
def main(args: Array[String]): Unit = {
println(Student.uniqueStudentNo())
val s=new Student("john",29)
//直接訪問伴生類Student中的私有成員
println(s.sex)
val s1=Student("john",29)
println(s1.name)
println(s1.age)
//使用內部類
val c1=new s1.Course("Scala",3.0f)
}
}
2 protected 成員
在java語言中,protected成員不但可以被該類及其子類訪問,也可以被同一個包中的其它類使用,但在scala中,protected成員只能被該類及其子類訪問
class SuperClass {
protected def f()=println(".....")
}
class SubClass extends SuperClass{
f()
}
class OtherClass{
//下面這個語句會報錯
//f()
}
3 無修飾符成員
無修飾符的成員同java 的public,可以在任何位置進行訪問
4 範圍保護
在scala中提供了更為靈活的訪問控制方法,private、protected除了可以直接修飾成員外,還可以以private[X]、protected[X]的方式進行更為靈活的訪問控制,這種訪問控制的意思是可以將private、protected限定到X,X可以是包、類,還可以是單例物件
package cn{
class UtilsTest{
//編譯通不過,因為Utils利用private[scala]修飾,只能在scala及其子包中使用
//Utils.toString()
}
package scala{
//private[scala]限定Utils只能在scala及子包中使用
private[scala] object Utils{
def toString(x:String){
println(x)
}
import cn.scala.xtwy._
def getTeacher():Teacher=new Teacher("john")
}
package xtwy{
class Teacher(var name:String) {
def printName()={Utils.toString(name)}
}
}
}
}
object appDemo{
import cn.scala._
import cn.scala.xtwy._
def main(args: Array[String]): Unit = {
//編譯通不過,同UtilsTest
//Utils.toString(new Teacher("john").name)
new Teacher("john").printName()
}
}
private[this],限定只有該類的物件才能訪問,稱這種成員為物件私有成員
package cn.scala.xtwy;
class Teacher(var name: String) {
private[this] def printName(tName:String="") :Unit= { println(tName) }
//呼叫private[this] printName方法
def print(n:String)=this.printName(n)
}
object Teacher{
//private[this]限定的成員,即使伴生物件Teacher也不能使用
//def printName=new Teacher("john").printName()
}
object appDemo {
def main(args: Array[String]): Unit = {
//編譯不能通過
//new Teacher("john").printName()
}
}
private,定義的類及伴生物件可以訪問
package cn.scala.xtwy;
class Teacher(var name: String) {
private def printName(tName:String="") :Unit= { println(tName) }
//可以訪問
def print(n:String)=this.printName(n)
}
object Teacher{
//伴生物件可以訪問
def printName=new Teacher("john").printName()
}
object appDemo {
def main(args: Array[String]): Unit = {
//不能訪問
//new Teacher("john").printName()
}
}
下面給出的是訪問規則表
修飾符 | 訪問範圍 |
---|---|
無任何修飾符 | 任何地方都可以使用 |
private[scala] | 在定義的類中可以訪問,在scala包及子包中可以訪問 |
private[this] | 只能在定義的類中訪問,即使伴生物件也不能訪問團 |
private | 在定義的的類及伴生物件中可以訪問,其它地方不能訪問 |
protected[scala] | 在定義的類及子類中可以訪問,在scala包及子包中可以訪問, |
protected[this] | 只能在定義的類及子類中訪問,即使伴生物件也不能訪問 |
protected | 在定義的類及子類中訪問,伴生物件可以訪問,其它地方不能訪問 |
包物件
包物件主要用於將常量、工具函式,使用時直接通過包名引用
//下面的程式碼給出了包物件的定義
package cn.scala.xtwy
//利用package關鍵字定義單例物件
package object Math {
val PI=3.141529
val THETA=2.0
val SIGMA=1.9
}
class Coputation{
def computeArea(r:Double)=Math.PI*r*r
}
上述程式碼編譯後會生成下列檔案:
對應位元組碼檔案如下:
D:\ScalaWorkspace\ScalaChapter08\bin\cn\scala\xtwy\Math>javap -private package.c
lass
Compiled from "Math.scala"
public final class cn.scala.xtwy.Math.package {
public static double SIGMA();
public static double THETA();
public static double PI();
}
D:\ScalaWorkspace\ScalaChapter08\bin\cn\scala\xtwy\Math>javap -private package$.
class
Compiled from "Math.scala"
public final class cn.scala.xtwy.Math.package$ {
public static final cn.scala.xtwy.Math.package$ MODULE$;
private final double PI;
private final double THETA;
private final double SIGMA;
public static {};
public double PI();
public double THETA();
public double SIGMA();
private cn.scala.xtwy.Math.package$();
}
不能看出,它為我們的包物件Math建立了一個資料夾,然後建立了兩個類,通過單例的方式實現方法呼叫。
import高階特性
1 隱式引入
在集合那一講,我們提到,如果不引入任何包,scala會預設引入java.lang._
scala._
Predef._
包中或物件中所有的類和方法,稱這種引入會隱式引入
2 重新命名
scala中允許對引入的類或方法進行重新命名,如果我們需要在程式中同時使用java.util.HashMap及scala.collection.mutable.HashMap時,可以利用重新命名的方法消除命名衝突的問題,雖然也可以採用包名字首的方式使用,但程式碼不夠簡潔
//將java.util.HashMap重新命名為JavaHashMap
import java.util.{ HashMap => JavaHashMap }
import scala.collection.mutable.HashMap
object RenameUsage {
def main(args: Array[String]): Unit = {
val javaHashMap = new JavaHashMap[String, String]()
javaHashMap.put("Spark", "excellent")
javaHashMap.put("MapReduce", "good")
for(key <- javaHashMap.keySet().toArray){
println(key+":"+javaHashMap.get(key))
}
val scalaHashMap=new HashMap[String,String]
scalaHashMap.put("Spark", "excellent")
scalaHashMap.put("MapReduce", "good")
scalaHashMap.foreach(e=>{
val (k,v)=e
println(k+":"+v)
})
}
}
3 類隱藏
//通過HashMap=> _,這樣類便被隱藏起來了
import java.util.{HashMap=> _,_}
import scala.collection.mutable.HashMap
object RenameUsage {
def main(args: Array[String]): Unit = {
//這樣的話,HashMap便無歧義地指向scala.collection.mutable.HashMap
val scalaHashMap=new HashMap[String,String]
scalaHashMap.put("Spark", "excellent")
scalaHashMap.put("MapReduce", "good")
scalaHashMap.foreach(e=>{
val (k,v)=e
println(k+":"+v)
})
}
內部類
前面我們提到過內部類,我們看到內部類可以像類的其它成員一樣訪問外部類的私有成員,本小節將對內部類進行更加詳細的介紹。
要點1:外部類不能訪問內部類的成員域,但內部類可以直接訪問外部類成員域,哪怕這個成員域是private私有的
class OuterClass {
//即使定義為 private[this] var x:Int=0,也是可行的
private var x:Int=0
//def getInnerY=y,外部類不能直接訪問內部類的成員域
class InnerClass{
private var y:Int=0
//內部的類可以直接訪問外部類的成員變數和成員方法,注意外部類的成員變數是private
def getOuterX= x
}
}
object AppDemo{
def main(args: Array[String]): Unit = {
val o=new OuterClass
//建立內部類物件,通過o.InnerClass方式,InnerClass就像是OuterClass的成員變數一樣
val i=new o.InnerClass
println(i.getOuterX)
}
}
從上述程式碼可以看出,內部類除了是一個類之外,與外部類的成員沒有任何區別,它可以與外部類的成員域一樣被使用
新增公眾微訊號,可以瞭解更多最新Spark、Scala相關技術資訊