Scala入门到精通——第八节 包和引入

本节主要内容

  1. 包的作用与定义
  2. 包的作用域与引入(import)的使用方法
  3. 访问控制
  4. 包对象
  5. import高级特性
  6. 内部类

包的作用与定义

同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,代码组织如下:
Scala入门到精通——第八节 包和引入

可以看到我们将代码放在默认包下面,但编码后的字节码文件被自动组织到cn.scala.xtwy文件夹当中,如下图
Scala入门到精通——第八节 包和引入
可以看出,我们可以在任何地方进行包中类的定义,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编译后产生的包文件:
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
}

上述代码编译后会生成下列文件:
Scala入门到精通——第八节 包和引入

对应字节码文件如下:

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相关技术资讯
Scala入门到精通——第八节 包和引入

上一篇:Scala入门到精通——第七节:类和对象(二)


下一篇:Scala入门到精通—— 第二节Scala基本类型及操作、程序控制结构