Scala面向對象程式設計綜述
1.Scala是純粹的面向對象的語言
Scala 是純粹的面向對象的語言,每個值都是對象,每個操作都是方法調用。
2.Scala大量重用了Java中的類型
Scala 與 Java 完全相容,Scala 程式會被編譯成 Java 位元組碼,通路 Java 字段,繼承 Java 類,實作 Java 接口都不需要特别的文法。
3…Scala OOP 比較 Java OOP 的主要差異
(1)Scala取消了接口(Interface),新增了類似的特質概念(Trait)。
(2)Scala 取消靜态字段和方法,新增單例對象的概念(Singleton Object)。
(3)Scala 新增了樣例類(case class)
Scala類和對象
類是對對象的抽象模闆,描述了該類對象可以擁有的屬性和方法
對象是類的具體執行個體。
1.類的基本概念
(1)類通過class關鍵字定義
(2)類通過new關鍵字建立執行個體
(3)類擁有成員變量和方法
(4)類的成員預設為public,也支援private、protected
(5)類中無法定義靜态成員變量和方法
(6)類無需明确定義構造方法,通過構造參數清單聲明為類的一部分
2.類的定義
(1)構造器
所有構造器都沒有傳回值。
①主構造器
主構造器便是“類名(構造參數清單)”
//主構造器的參數清單直接定義在類名後面
class Point(xc: Int, yc: Int) {
var x: Int = xc //成員變量
var y: Int = yc
②輔助構造器
輔助構造器“this(構造參數清單)”,類似于 Java 中的構造方法重載
注意輔助構造器必須間接或直接調用主構造器。
def this() ={
this(0, 0) //第一句必須調用其他構造器
}
(2)成員變量和方法
①類中可以有字段和方法,統稱為成員變量。字段可以用 var 或 val 定義,都是指向對象的變量,var 字段表示可以重新指派,val 不能。
//成員方法
def move(dx: Int, dy: Int) ={
x = x + dx
y = y + dy
}
}
②在方法不需要參數時可以定義成下面兩種方法:
對于無參數方法的方法形式 obj.width 又像是在直接引用 obj 對象的width 屬性,這種統一性就叫做統一通路原則。
def width():Int=x //空括号方法,調用方式:obj.width 或者 obj.width()
def width:Int=x //無參數方法,調用方式:obj.width
③在 Scala 中,嚴格來說是沒有 getter 和 setter 這個說法,使用了“value”和“value_=”兩個方法來分别代替了 getter 和 setter。
class Counter(var value:Int)
class Counter{
private var privateValue = 0;//私有變量,外界無法直接通路
def value = privateValue;//定義一個方法,代替 getter
def value_= ( newValue : Int ){// value_= 是方法名字,代替 setter
value = newValue;
} //注意,scala 中預設方法是 public 的 }
(3)類的執行個體化
使用 new 關鍵字執行個體化一個類。
var p=new Point()
p.x
p.y
p=new Point(12,11)
p.x
p.y
p.move(1,2)
(4)類的繼承
①Scala使用“extends”關鍵字實作繼承
②子類重寫父類方法必須使用“override”關鍵字
class Person {
override def toString = getClass.getName +"name="
}
class Employee extends Person {
override def toString = super.toString
}
val p=new Person
p.name="zhangsan"
println(p.toString)
val e=new Employee
e.name="lisi"
println(e.toString)
println(p.isInstanceOf[Person]) //true
println(p.isInstanceOf[Employee]) //false
println(e.isInstanceOf[Person]) //true
println(e.isInstanceOf[Employee]) //true
p.asInstanceOf[Employee] //編譯失敗
isInstanceOf[T]判斷是否為某一類型,asInstanceOf[T]進行強制類型轉換。
③向上轉型
def todo(x:Person)={
println(x.toString)
}
todo(p) //編譯通過
todo(e) //編譯通過
④向下轉型
def todo(x:Person)={
println(x.asInstanceOf[Employee].toString)
}
todo(p) //編譯失敗
todo(e) //編譯通過
3.抽象類
(1)抽象類可包含未實作的方法、即抽象方法
(2)抽象類無法執行個體化
(3)抽象類使用“abstract”關鍵字修飾
①子類重寫父類抽象方法時,“override”關鍵字可選
abstract class Shape{
def draw():Unit
}
②子類重寫父類非抽象方法,“override”關鍵字必寫
class Square extends Shape{
override def draw():Unit={
println("draw a square")
}
}
var shape=new Square
shape.draw
4.單例對象
(1)Object與class
①單例對象是一種特殊的類,有且隻有一個執行個體
②單例對象在第一次使用時被建立,單例對象不需要也不能再被執行個體化
③單例對象中的所有成員都可以直接通路,main 方法必須定義在單例對象中。
object HelloWorld {
println("我被建立")
def main(args: Array[String]): Unit = {
println("HelloWorld")
}
println("我被執行")
}
簡單方式建立:
object MyApp extends App{
println("Hello World")
}
因為所繼承的 App(内置特質)中已經提供了 main()方法的實作。
④類和單例對象的差別:
單例對象不能帶參數,而類可以。因為單例對象無法使用 new 關鍵字執行個體化,也就沒有辦法為它傳遞執行個體化參數。
(2)伴生(companion)
①伴生關系
限制條件:object(單例對象)與 class(普通類)同名且在一個檔案中。
一旦建立伴生關系,伴生對象與伴生類可互相通路私有成員。伴生對象主要為伴生類提供一種靜态成員通路的途徑
//伴生類
class Student(n: String, a: Int) {
private var name = n //私有變量,伴生對象可以通路
private var age = a
}
//伴生對象
object Student {
def apply(n: String, a: Int): Student = new Student(n, a)
def main(args: Array[String]): Unit = {
val stu=Student("Jason",9) //通過伴生對象的 apply()方法建立執行個體
println(stu.name)
} }
②伴生類
指在伴生關系中的伴生類,在 class(伴生類)中通路 object(伴生對象)的私有屬性,使用“object 名.成員”。
③指在伴生關系中的單例對象,在 object(伴生對象)中通路 class(伴生類)的私有屬性,必須通過 class 執行個體對象通路。如上面的“stu.name
④apply()的方法的調用
def apply(n: String, a: Int): Student = new Student(n, a)
val stu=Student("Jason",9) //等價于 val stu=Student.apply("Jason",9)
5.特質(Trait)
(1)概述:
①Scala中沒有接口(interface)的概念
②特質用于在類之間共享程式接口和字段,類似Java接口
③特質是字段和方法的集合,可以提供字段和方法實作
④類和單例對象都可以擴充特質(extends)
⑤特質不能被執行個體化,是以沒有構造參數,類似Java接口
⑥實作特質中的方法使用“override”
(2)使用特質
import scala.collection.mutable.ArrayBuffer
trait Pet {
val name: String
def cry():Unit
}
class Dog(val name: String) extends Pet{
override def cry()=println("wow ...")
}
val dog = new Dog("Harry")
val animals = ArrayBuffer.empty[Pet]
animals.append(dog)
animals.foreach(pet => {println(pet.name);pet.cry()}) // Prints Harry wow ...
(3)混入特質(mixin)
①當某個特質被用于組合類時,被稱為混入
②一個類隻能有一個父類但是可以有多個混入(分别使用關鍵字extends和with)
abstract class A {
val message: String
}
class B extends A {
val message = "I'm an instance of class B"
}
trait C extends A {
def loudMessage = message.toUpperCase()
}
//構造順序由左往右,如果前面已經構造了某個父類,後面子類的該父類不會重複構造
class D extends B with C
val d = new D
println(d.message) // I'm an instance of class B
println(d.loudMessage) // I'M AN INSTANCE OF CLASS B
(4)動态混入特質
class Drawing {
//this:Type=> 自身類型,表示該類執行個體化時必須混入相應特質或子特質,self是this的别名。
self: Shape =>
def start(): Unit = draw()
}
trait Shape {
def draw(): Unit
}
trait Square extends Shape {
def draw(): Unit = println("draw a square")
}
trait Triangle extends Shape {
def draw(): Unit = println("draw a triangle")
}
//動态混入
(new Drawing() with Square).start()
(new Drawing() with Triangle).start()
(5)特質與抽象類的選擇
①優先使用特質
抽象類隻能繼承一次
特質可混入多個
②需要使用帶參構造方法時,使用抽象類
③與Java互操作性
抽象類與Java完全可互操作
特質隻有在不包含任何實作代碼時才可互操作
6.内部類
一個類可以作為另一個類的成員,稱為内部類
(1)Java内部類是外部類的成員
class Graph {
class Node {
var connectedNodes: List[Graph#Node] = Nil
def connectTo(node: Graph#Node) {
if (connectedNodes.find(node.equals).isEmpty) {
connectedNodes = node :: connectedNodes
}
}
}
var nodes: List[Node] = Nil
def newNode: Node = {
val res = new Node
nodes = res :: nodes
res
}
}
(2)Scala内部類綁定到外部類的對象執行個體
class Graph {
class Node {
var connectedNodes: List[Node] = Nil
def connectTo(node: Node) {
if (connectedNodes.find(node.equals).isEmpty) {
connectedNodes = node :: connectedNodes
}
}
}
var nodes: List[Node] = Nil
def newNode: Node = {
val res = new Node
nodes = res :: nodes
res
}
}
val g: Graph = new Graph
val n1: g.Node = g.newNode
val n2: g.Node = g.newNode
n1.connectTo(n2) // legal
val h: Graph = new Graph
val n3: h.Node = h.newNode
//n1與n3被認為是不同的類型
n1.connectTo(n3) // illegal!
7.樣例類
樣例類常用于描述不可變的值對象(Value Object)
case class Student(name:String,age:Int) //定義樣例類
val stu=Student("Jason",19) //建立樣例類的執行個體,無需new關鍵字
println(stu.name) //通路對象屬性
(1)樣例類構造參數預設聲明為“val”,自動實作類構造參數的getter
(2)樣例類構造參數聲明為“var”時,自動實作類構造參數的setter和getter
(3)樣例類自動建立伴生對象
(4)樣例類自動實作的其他方法:
①toString()、equals()、copy()、hashCode()
②伴生對象中的apply()、unapply():unapply()接受一個對象,從對象中提取出相應的值,主要用于模式比對中。
(5)樣例類與普通的差別:
①樣例類通常用于描述不可變的資料,資料完全依賴構造參數。
②樣例類預設可用伴生對象方式建立執行個體,普通類需要定義 apply()。
③樣例類預設支援模式比對,普通類需要定義 unapply()。
④兩個樣例類“==”操作時,通過按值比較而不是按引用
⑤樣例類操作更簡單
8.樣例類與枚舉
(1)枚舉(Enumeration)的定義
object Weekday extends Enumeration {
//枚舉值從0開始計數
val Mon,Tue,Wed,Thu,Fri,Sat,Sun=Value
}
//枚舉的使用
Weekday.Sun
Weekday.Sun.id //擷取枚舉值的計數值
Weekday.values.foreach(println)
(2)樣例類與枚舉差別
①枚舉更簡單,代碼更少
②樣例類的字段比枚舉的值更強大
③樣例類可擴充
abstract class Term(code: String)
case class Var(name: String) extends Term(name)
case class Fun(arg: String, body: Term) extends Term(arg)
case class App(f: Term, v: Term) extends Term("App")
9.泛型類
(1)泛型類指可以接受類型參數的類,泛型類在集合類中被廣泛使用
(2)定義泛型類使用“[]”
class Stack[T] {
var elements: List[T] = Nil
def push(x: T) { elements = x :: elements }
def top: T = elements.head
def pop() {
var t = elements.head
elements = elements.tail
t
}
def showElements(){
elements.foreach(x=>print(s"$x "));println()}
}
val ms = new Stack[Int]()
ms.push(10)
ms.showElements()
ms.push(20)
ms.showElements()
val t = ms.pop()
ms.showElements()
10.類型邊界
(1)在Scala中,類型參數可以有一個類型邊界限制
(2)類型上界:将類型限制為另一種類型的子類
①T<:A 表示類型變量T應該是類型A的子類
②A是具體類型,T是泛型
abstract class Animal { def name: String }
abstract class Pet extends Animal {}
class Cat extends Pet { override def name: String = "Cat" }
class Dog extends Pet { override def name: String = "Dog" }
class Lion extends Animal { override def name: String = "Lion" }
class PetContainer[P <: Pet](p: P) { def pet: P = p }
val dogContainer = new PetContainer[Dog](new Dog) //編譯通過
val catContainer = new PetContainer[Cat](new Cat) //編譯通過
val lionContainer = new PetContainer[Lion](new Lion) //編譯錯誤
(3)類型下界:将類型聲明為另一種類型的超類
①T>:A 表示類型變量T應該是類型A的超類
②A是具體類型,T是泛型
abstract class Animal { def name: String }
abstract class Pet extends Animal {}
class Cat extends Pet { override def name: String = "Cat" }
class Dog extends Pet { override def name: String = "Dog" }
class Lion extends Animal { override def name: String = "Lion" }
class PetContainer[P >: Lion](p: P) { def pet: P = p }
val dogContainer = new PetContainer[Dog](new Dog) //編譯錯誤
val catContainer = new PetContainer[Cat](new Cat) //編譯錯誤
val lionContainer = new PetContainer[Lion](new Lion) //編譯通過
11.型變
型變定義了泛型的繼承方式
(1)協變
List 類型是協變類型
對于兩種類型 A 和 B,如果 A 是 B 的子類型,那麼 Foo[A] 就是 Foo[B] 的子類型
(2)逆變
對于兩種類型 A 和 B,如果 A 是 B 的子類型,那麼 Bar[B] 就是 Bar[A] 的子類型
(3)不變
預設情況下,Scala中的泛型類是不變的