Scala入門到精通——第二十二節 高級類型 (一)
作者:搖擺少年夢
視頻地址:http://www.xuetuwuyou.com/course/12
本節主要內容
- this.type使用
- 類型投影
- 結構類型
- 復合類型
1. this.type使用
class Person{
private var name:String=null
private var age:Int=0
def setName(name:String)={
this.name=name
//返回對象本身
this
}
def setAge(age:Int)={
this.age=age
//返回對象本身
this
}
override def toString()="name:"+name+" age:"+age
}
object Main extends App{
//鏈式調用
println(new Person().setAge(18).setName("搖擺少年夢"))
}
當涉及到繼承時,這樣的機制會存在些問題,比如:
class Person{
private var name:String=null
private var age:Int=0
def setName(name:String)={
this.name=name
//返回Person對象本身
this
}
def setAge(age:Int)={
this.age=age
//返回Person對象本身
this
}
override def toString()="name:"+name+" age:"+age
}
class Student extends Person{
private var studentNo:String=null
def setStudentNo(no:String)={
this.studentNo=no
this
}
override def toString()=super .toString()+" studetNo:"+studentNo
}
object Main extends App{
//以下的代碼會報錯
//value setStudentNo is not a member of cn.scala.xtwy.advancedtype.Person
println(new Student().setName("john").setAge(22).setStudentNo("2014"))
}
Student對象在調用setName、setAge方法時,返回的對象類型實質上仍然是Person類型。而Person類型並沒有setStudentNo方法,從而編譯出錯。為解決該問題,能夠將setName、setAge方法的返回值設置為:this.type
,代碼例如以下:
class Person{
private var name:String=null
private var age:Int=0
//this.type返回實際類型
def setName(name:String):this.type={
this.name=name
this
}
def setAge(age:Int):this.type={
this.age=age
this
}
override def toString()="name:"+name+" age:"+age
}
class Student extends Person{
private var studentNo:String=null
def setStudentNo(no:String)={
this.studentNo=no
this
}
override def toString()=super.toString()+" studetNo:"+studentNo
}
object Main extends App{
//println(new Person().setAge(18).setName("搖擺少年夢"))
println(new Student().setName("john").setAge(22).setStudentNo("2014"))
}
2. 類型投影
我們知道,Scala中的內部類同類成員一類,僅僅只是它被定義一個類而已。它具有例如以下的訪問創建方式:
class Outter{
private var x:Int=0
//內部類Inner
class Inner{
def test()=x
}
}
object TypeProject extends App{
val outter=new Outter
//創建內部類的方式。同訪問正常的成員變量一樣
val inner=new outter.Inner
println(inner.test())
}
那Scala語言中不同對象創建的內部類是不是同一個類呢?事實上不是,以下的代碼就是證明:
import scala.reflect.runtime.universe.typeOf
class Outter{
private var x:Int=0
def print(i:Inner)=i
class Inner{
def test()=x
}
}
object TypeProject extends App{
val outter=new Outter
val inner=new outter.Inner
val outter2=new Outter
val inner2=new outter2.Inner
//以下的代碼編譯會失敗
//outter.print(inner2)
//這是由於不同outter對象相應的內部類成員類型是不一樣的
//這就跟兩個類成員的實例它們內存地址不一樣相似
//以下的類型推斷會輸出false
//這也進一步說明了它們類型是不一樣的
println(typeOf[outter.Inner]==typeOf[outter2.Inner])
}
上述代碼中Outter類中的def print(i:Inner)=i
成員方法中的參數類型Inner事實上相當於def print(i:this.Inner)=i
或def print(i:Outter.this.Inner)=i
,也即它依賴於外部類。總體的話構成了一路徑。由於也稱為路徑依賴類型。
再看下內部類的幾種使用情況。它相應幾種不同的路徑依賴類型:
(1)類內部本身使用情況
class Outter{
private var x:Int=0
//內部使用,相當於
//private var i:Inner=new Outter.this.Inner
private var i:Inner=new Inner
def print(i:Inner)=i
class Inner{
def test()=x
}
}
(2)子類使用父類中的內部類
class Outter{
private var x:Int=0
def print(i:Inner)=i
class Inner{
def test()=x
}
}
//子類中使用父類中的內部類
class A extends Outter{
private val i=new A.super.Inner
}
(3)在其他類或對象中使用
object TypeProject extends App{
val outter=new Outter
val inner=new outter.Inner
val outter2=new Outter
val inner2=new outter2.Inner
}
明確幾種路徑依賴類型之後,我們能夠對類型投影進行說明:類型投影的目的是將外部類Outter中定義的方法def print(i:Inner)=i。它能夠接受做隨意外部類對象中的Inner類。
上面的樣例當中outter與outter2中的Inner類型具有共同的父類。
例如以下圖所看到的:
代碼例如以下:
import scala.reflect.runtime.universe.typeOf
class Outter{
private var x:Int=0
private var i:Inner=new Outter.this.Inner
//Outter#Inner類型投影的寫法
//能夠接受不論什麽outter對象中的Inner類型對象
def print(i:Outter#Inner)=i
class Inner{
def test()=x
}
}
class A extends Outter{
private val i=new A.super.Inner
}
object TypeProject extends App{
val outter=new Outter
val inner=new outter.Inner
val outter2=new Outter
val inner2=new outter2.Inner
//以下的這個語句能夠成功運行
outter.print(inner2)
//註意,以下的這條語句返回的仍然是false。我們僅僅是對print方法中的
//參數進行類型投影,並沒有改變outter.Inner與outter2.Inner
//是不同類的事實
println(typeOf[outter.Inner]==typeOf[outter2.Inner])
}
3. 結構類型
結構類型(Struture Type)通過利用反射機制為靜態語言加入動態特性。從面使得參數類型不受限於某個已命名的的類型,比如:
object StructureType {
//releaseMemory中的方法是一個結構體類型,它定義了
//一個抽象方法,對close方法的規格進行了說明
def releaseMemory(res:{def close():Unit}){
res.close()
}
def main(args: Array[String]): Unit = {
//結構體使用方式
releaseMemory(new {def close()=println("closed")})
}
}
另外結構體類型還能夠用type關鍵字進行聲明,如:
object StructureType {
def releaseMemory(res:{def close():Unit}){
res.close()
}
//採用關鍵字進行結構體類型聲明
type X={def close():Unit}
//結構體類型X作為類型參數。定義函數releaseMemory2
def releaseMemory2(x:X)=x.close()
def main(args: Array[String]): Unit = {
releaseMemory(new {def close()=println("closed")})
//函數使用同releaseMemory
releaseMemory2(new {def close()=println("closed")})
}
}
從上面的代碼來看,結構體類型事實上能夠看作是一個類,在函數調用時,直接通過new操作來創建一個結構體類型對象,當然它是匿名的。因此,上述方法也能夠傳入一個實現了close方法的類或單例對象
//定義一個普通的scala類,當中包括了close成員方法
class File{
def close():Unit=println("File Closed")
}
//定義一個單例對象,當中也包括了close成員方法
object File{
def close():Unit=println("object File closed")
}
object StructureType {
def releaseMemory(res:{def close():Unit}){
res.close()
}
type X={def close():Unit}
def releaseMemory2(x:X)=x.close()
def main(args: Array[String]): Unit = {
releaseMemory(new {def close()=println("closed")})
releaseMemory2(new {def close()=println("closed")})
//對於普通的scala類,直接創建對象傳入就可以使用前述的方法
releaseMemory(new File())
//對於單例對象,直接傳入單例對象就可以
releaseMemory(File)
}
}
我們能夠看到,盡管說定義的方法中的參數是一個結構體類型,可是我們也能夠傳入普通類對象和單例對象,僅僅要該對象或類中具有結構體類型中聲明的方法就可以。上述代碼也告訴 我們。事實上結構體類型也是一個類。僅僅是表現形式與類有所差別而已。
4. 復合類型
復合類型在前面的課程中事實上我們已經有過接觸。比如
class B extends A with Cloneable
總體 A with Cloneable能夠看作是一個復合類型。它也能夠通過type關鍵字來進行聲明,比如:
class B extends A with Cloneable
object CompoundType {
//利用關鍵字type聲明一個復合類型
type X=A with Cloneable
def test(x:X)=println("test ok")
def main(args: Array[String]): Unit = {
test(new B)
}
}
加入公眾微信號。能夠了解很多其他最新Spark、Scala相關技術資訊
Scala入門到精通——第二十二節 高級類型 (一)