Go語(yǔ)言程序設(shè)計(jì)及實(shí)例 課件 第3章 Go語(yǔ)言面向?qū)ο缶幊蘝第1頁(yè)
Go語(yǔ)言程序設(shè)計(jì)及實(shí)例 課件 第3章 Go語(yǔ)言面向?qū)ο缶幊蘝第2頁(yè)
Go語(yǔ)言程序設(shè)計(jì)及實(shí)例 課件 第3章 Go語(yǔ)言面向?qū)ο缶幊蘝第3頁(yè)
Go語(yǔ)言程序設(shè)計(jì)及實(shí)例 課件 第3章 Go語(yǔ)言面向?qū)ο缶幊蘝第4頁(yè)
Go語(yǔ)言程序設(shè)計(jì)及實(shí)例 課件 第3章 Go語(yǔ)言面向?qū)ο缶幊蘝第5頁(yè)
已閱讀5頁(yè),還剩84頁(yè)未讀, 繼續(xù)免費(fèi)閱讀

下載本文檔

版權(quán)說(shuō)明:本文檔由用戶提供并上傳,收益歸屬內(nèi)容提供方,若內(nèi)容存在侵權(quán),請(qǐng)進(jìn)行舉報(bào)或認(rèn)領(lǐng)

文檔簡(jiǎn)介

第3章Go語(yǔ)言面向?qū)ο缶幊堂嫦驅(qū)ο蟮母拍?1類(lèi)與對(duì)象1.類(lèi)2.對(duì)象類(lèi)與對(duì)象1.類(lèi)現(xiàn)實(shí)世界中的各種事物都可以分類(lèi),例如,星球、動(dòng)物、房子、學(xué)生、汽車(chē)等。類(lèi)包含屬性、方法和事件,通過(guò)屬性表示它的特征(數(shù)據(jù)),通過(guò)方法實(shí)現(xiàn)它的行為(功能),通過(guò)事件做出響應(yīng)。類(lèi)可以派生出子類(lèi)(派生類(lèi)),派生子類(lèi)的類(lèi)稱為父類(lèi)。對(duì)于一個(gè)系統(tǒng)來(lái)說(shuō),其最基本的類(lèi)稱為基類(lèi),由基類(lèi)派生出多個(gè)類(lèi),這些類(lèi)還可以繼續(xù)派生出更多的子類(lèi),形成類(lèi)的層次結(jié)構(gòu)。例如:基類(lèi)——汽車(chē)子類(lèi):卡車(chē)、轎車(chē)、客車(chē)等汽車(chē)類(lèi)屬性:車(chē)輪、方向盤(pán)、發(fā)動(dòng)機(jī)、車(chē)門(mén)等汽車(chē)類(lèi)方法:前進(jìn)、倒退、剎車(chē)、轉(zhuǎn)彎、聽(tīng)音樂(lè)、導(dǎo)航等汽車(chē)類(lèi)事件:車(chē)胎漏氣、油用到臨界、遇到碰撞等類(lèi)與對(duì)象2.對(duì)象對(duì)象是類(lèi)的具體化,是具有屬性和方法的實(shí)體(實(shí)例)。對(duì)象通過(guò)唯一的標(biāo)識(shí)名區(qū)別于其他對(duì)象。對(duì)象通常還有固定的對(duì)外接口,它是對(duì)象與外界通信的通道。例如,汽車(chē)類(lèi)派生出的轎車(chē)子類(lèi)的對(duì)象有:比亞迪F6、奧迪A6L等。02面向?qū)ο缶幊堂嫦驅(qū)ο缶幊堂嫦驅(qū)ο缶幊蹋∣bject-OrientedProgramming,簡(jiǎn)稱OOP)是一種基于類(lèi)和對(duì)象的程序設(shè)計(jì)方法。它將需要解決的問(wèn)題抽象成一個(gè)個(gè)能以計(jì)算機(jī)邏輯形式表現(xiàn)的封裝實(shí)體,即對(duì)象,類(lèi)是在對(duì)象之上的抽象,通過(guò)定義屬性和方法來(lái)描述其特征和職責(zé)。類(lèi)為屬于它的全部對(duì)象提供了統(tǒng)一的抽象描述,可看作是一種抽象的數(shù)據(jù)類(lèi)型,是對(duì)象的模板;對(duì)象則是類(lèi)的具體化,是類(lèi)的實(shí)例,用接口來(lái)描述對(duì)象的地位以及對(duì)象實(shí)例之間的關(guān)系。由此構(gòu)成面向?qū)ο蟮母拍钅P腿鐖D,它能更好地反映現(xiàn)實(shí)世界,對(duì)事物的描述更加自然,使問(wèn)題的解決更容易。面向?qū)ο缶幊淘诿嫦驅(qū)ο蟪绦蛟O(shè)計(jì)中,數(shù)據(jù)結(jié)構(gòu)與作用于其上的算法被視為了一個(gè)整體,即為對(duì)象,而現(xiàn)實(shí)世界中任何類(lèi)的對(duì)象都具有一定的屬性和方法,可以用數(shù)據(jù)結(jié)構(gòu)與算法二者分別加以描述,所以可用下面的等式來(lái)定義面向?qū)ο蟪绦颍簩?duì)象=數(shù)據(jù)結(jié)構(gòu)+算法程序=對(duì)象+對(duì)象+…03面向?qū)ο笳Z(yǔ)言的特征1.封裝2.繼承3.多態(tài)面向?qū)ο笳Z(yǔ)言的特征1.封裝所謂“封裝”,也就是用一個(gè)框架把數(shù)據(jù)和代碼組合在一起,形成對(duì)象。封裝的對(duì)象之間通過(guò)一種稱為“消息傳遞”的機(jī)制進(jìn)行通信。消息是向?qū)ο蟀l(fā)出的服務(wù)請(qǐng)求,它包含要求接收對(duì)象(接收者)去執(zhí)行某些活動(dòng)的信息,以及完成要求所需的其他信息(參數(shù))。發(fā)送消息的對(duì)象(發(fā)送者)不需要知道接收者如何對(duì)請(qǐng)求予以響應(yīng),接收者收到消息,它就承擔(dān)了執(zhí)行指定動(dòng)作的責(zé)任,通過(guò)執(zhí)行自身內(nèi)部封裝的某個(gè)方法來(lái)滿足發(fā)送者的請(qǐng)求,并作出答復(fù)。面向?qū)ο笳Z(yǔ)言的特征2.繼承世界本質(zhì)是復(fù)雜的,但在大千世界中事物之間又有很多相似之處,而這種相似性正是人們理解紛繁事物的一個(gè)基礎(chǔ)。相似的事物之間往往具有某種“繼承”關(guān)系,比如,兒子長(zhǎng)得像父親,是因?yàn)閮鹤拥幕蚶^承了父親的許多遺傳特性;卡車(chē)、轎車(chē)、客車(chē)存在相似性,是因?yàn)樗鼈兌紝儆谄?chē),繼承了汽車(chē)的一般特征?!袄^承”是面向?qū)ο蠓椒ǖ囊粔K基石,通過(guò)它可以建立起具有等級(jí)層次的類(lèi)的體系。例如,先創(chuàng)建一個(gè)通用的汽車(chē)類(lèi),定義汽車(chē)的一般屬性(車(chē)輪、方向盤(pán)、發(fā)動(dòng)機(jī)、車(chē)門(mén)等)和操作方法(前進(jìn)、倒退、剎車(chē)、轉(zhuǎn)彎等),再?gòu)倪@個(gè)已有的類(lèi)出發(fā),用繼承的方式派生出新的子類(lèi),如卡車(chē)、轎車(chē)、客車(chē)等,見(jiàn)圖。它們都是比汽車(chē)類(lèi)更具體的類(lèi),每個(gè)具體的類(lèi)都可以增加自己特有的一些屬性和方法。在面向?qū)ο笙到y(tǒng)設(shè)計(jì)中,繼承關(guān)系更一般的表示如圖。面向?qū)ο笳Z(yǔ)言的特征另外,繼承也是父類(lèi)與子類(lèi)之間共享數(shù)據(jù)和方法的一個(gè)重要途徑。如果一個(gè)類(lèi)有兩個(gè)或以上的直接父類(lèi),這樣的繼承結(jié)構(gòu)稱為多重繼承或多繼承。現(xiàn)實(shí)中這種模型也屢見(jiàn)不鮮,如一些類(lèi)似于沙發(fā)床的組合功能產(chǎn)品,既有沙發(fā)的功能,又有床的功能,應(yīng)當(dāng)允許它同時(shí)繼承自沙發(fā)和床這兩個(gè)類(lèi),如圖。多繼承更一般的表示見(jiàn)圖。面向?qū)ο笳Z(yǔ)言的特征3.多態(tài)多態(tài)是指同一個(gè)消息或操作在作用于不同對(duì)象時(shí),可以有不同的反應(yīng),產(chǎn)生不同的行為和結(jié)果。舉個(gè)例子:春秋時(shí)期,孔子率眾弟子周游列國(guó)因其“仁”的主張得不到采納而被各國(guó)驅(qū)逐出境,有一次在荒野里餓得走不動(dòng)了,于是問(wèn)他的弟子為何會(huì)落到這步田地“匪兕匪虎,率彼曠野。吾道非耶,吾何為于此?”子路以為“是不是由于我們沒(méi)有仁德和智謀,所以人們不采納我們的主張?”子貢認(rèn)為“先生的道(理想)大到了極點(diǎn)以致天下人所不容,倒不如降低自己的理想以求安身?!鳖伝貏t堅(jiān)持“即使天下人都不容我們,但只要理想是正確的,不被世人接納也沒(méi)關(guān)系,走自己的路方顯君子本色!”

——見(jiàn)《史記·孔子世家》陳蔡之厄的典故第3章Go語(yǔ)言面向?qū)ο缶幊堂嫦驅(qū)ο笤贕o中的實(shí)現(xiàn)01封裝的實(shí)現(xiàn)1.屬性2.方法3.屬性訪問(wèn)4.對(duì)象創(chuàng)建封裝的實(shí)現(xiàn)1.屬性Go語(yǔ)言并沒(méi)有用于表示類(lèi)的class關(guān)鍵字,但可以使用結(jié)構(gòu)體(struct)實(shí)現(xiàn)對(duì)屬性的封裝,形式為:type類(lèi)名struct{

屬性1類(lèi)型1

屬性2類(lèi)型2 ...

屬性n類(lèi)型n}例如,定義一個(gè)人類(lèi)Human,包括姓名、身高、體重、年齡4個(gè)屬性,如下:typeHumanstruct{ namestring heightfloat32 weightfloat32 ageint}封裝的實(shí)現(xiàn)2.方法Go類(lèi)的方法不像其他面向?qū)ο笳Z(yǔ)言那樣寫(xiě)在類(lèi)體內(nèi)部,而是一個(gè)以指針作用于類(lèi)上的外部函數(shù),形如:func(指針名*類(lèi)名)方法名([形參列表])[(返回值列表)]{

方法體 [return[值列表]]}例如,給人類(lèi)Human定義一個(gè)計(jì)算BMI(BodyMassIndex,身體質(zhì)量指數(shù))的方法bmiCal,如下:func(h*Human)bmiCal()float32{ returnh.weight/(h.height*h.height)}封裝的實(shí)現(xiàn)3.屬性訪問(wèn)在面向?qū)ο蟮木幊谭绞较拢獠砍绦蛲ǔ2唤ㄗh直接訪問(wèn)類(lèi)內(nèi)部的屬性,而是通過(guò)類(lèi)提供的一對(duì)get/set方法來(lái)獲取和設(shè)置屬性值,即所謂的通過(guò)“模型(值對(duì)象)”操作數(shù)據(jù),很多持久化框架也是基于這樣的規(guī)范來(lái)編寫(xiě)程序的。Go類(lèi)的get/set方法的定義方式與普通方法一樣,但建議將它們分別命名為getXxx、setXxx(其中Xxx為屬性名,首字母大寫(xiě))。例如,定義一對(duì)獲取與設(shè)置人類(lèi)體重的get/set方法,如下://get方法func(h*Human)getWeight()float32{ returnh.weight //獲取體重}//set方法func(h*Human)setWeight(weightfloat32){ h.weight=weight //設(shè)置體重}這樣在程序中就可以通過(guò)這對(duì)方法來(lái)設(shè)置和獲取一個(gè)人的體重值,如下:man:=Human{} //創(chuàng)建人類(lèi)的對(duì)象(一個(gè)人)man.setWeight(60) //設(shè)置他(她)的體重fmt.Println(man.getWeight()) //60封裝的實(shí)現(xiàn)4.對(duì)象創(chuàng)建創(chuàng)建類(lèi)的對(duì)象,通常有如下幾種寫(xiě)法:對(duì)象名:=類(lèi)名{}對(duì)象名:=new(類(lèi)名)var對(duì)象名類(lèi)名=類(lèi)名{}在創(chuàng)建對(duì)象后,再通過(guò)定義好的一系列set方法給對(duì)象的各屬性賦初值,形如:對(duì)象名.setXxx1(屬性1值)對(duì)象名.setXxx2(屬性2值)...對(duì)象名.setXxxn(屬性n值)當(dāng)然,也可以在創(chuàng)建對(duì)象的同時(shí)就初始化其各個(gè)屬性的值,比如只寫(xiě)一句:對(duì)象名:=類(lèi)名{屬性1值,屬性2值,...,屬性n值}封裝的實(shí)現(xiàn)【實(shí)例3.1】用面向?qū)ο蠓椒ǚ庋b一個(gè)人的身高、體重等屬性,并計(jì)算其BMI值。程序代碼如下(human.go):運(yùn)行結(jié)果如圖。02繼承的實(shí)現(xiàn)繼承的實(shí)現(xiàn)Go語(yǔ)言沒(méi)有用于聲明繼承關(guān)系的extends關(guān)鍵字,而是采用在結(jié)構(gòu)體中內(nèi)嵌類(lèi)型的方式來(lái)實(shí)現(xiàn)繼承,即讓子類(lèi)包含其父類(lèi)名稱的屬性,形式如下:type父類(lèi)名struct{

屬性1類(lèi)型1

屬性2類(lèi)型2 ...

屬性n類(lèi)型n}

type子類(lèi)名struct{

父類(lèi)名

屬性n+1類(lèi)型n+1

屬性n+2類(lèi)型n+2 ...

屬性n+n類(lèi)型n+n}這樣繼承父類(lèi)后,子類(lèi)不僅自動(dòng)“擁有”父類(lèi)原來(lái)的所有屬性(屬性1~屬性n),還可以增加定義一些自己特有的屬性(如上面的屬性n+1~屬性n+n),同時(shí),原來(lái)定義在父類(lèi)上的方法也會(huì)自動(dòng)屬于子類(lèi),當(dāng)然也可以另外增加定義一些子類(lèi)所特有的方法。繼承的實(shí)現(xiàn)【實(shí)例3.2】定義一個(gè)動(dòng)物類(lèi)Animal作為父類(lèi),人類(lèi)Human作為子類(lèi)繼承動(dòng)物類(lèi)的屬性,并增加一個(gè)物種(species)屬性,然后輸出一個(gè)具體的人的信息。程序代碼如下(animal.go):運(yùn)行結(jié)果如圖。03多態(tài)的實(shí)現(xiàn)1.定義接口2.實(shí)現(xiàn)方法3.動(dòng)態(tài)綁定多態(tài)的實(shí)現(xiàn)1.定義接口先定義一個(gè)接口,里面聲明(羅列出)需要實(shí)現(xiàn)多態(tài)的一系列方法:type接口名interface{

方法名1()類(lèi)型1

方法名2()類(lèi)型2 ...

方法名n()類(lèi)型n}多態(tài)的實(shí)現(xiàn)//第1個(gè)類(lèi)對(duì)接口中方法的實(shí)現(xiàn)func(指針名*類(lèi)名1)方法名1()類(lèi)型1{ ...//方法體}func(指針名*類(lèi)名1)方法名2()類(lèi)型2{ ...//方法體}...func(指針名*類(lèi)名1)方法名n()類(lèi)型n{ ...//方法體}

//第2個(gè)類(lèi)對(duì)接口中方法的實(shí)現(xiàn)func(指針名*類(lèi)名2)方法名1()類(lèi)型1{ ...//方法體}func(指針名*類(lèi)名2)方法名2()類(lèi)型2{ ...//方法體}...func(指針名*類(lèi)名2)方法名n()類(lèi)型n{ ...//方法體}2.實(shí)現(xiàn)方法接口僅提供方法聲明,并未有方法實(shí)現(xiàn),具體的實(shí)現(xiàn)則留給各個(gè)類(lèi)自己去完成,這里假設(shè)有兩個(gè)類(lèi),分別獨(dú)立實(shí)現(xiàn)上面接口中的方法如下:多態(tài)的實(shí)現(xiàn)3.動(dòng)態(tài)綁定編程時(shí),可聲明一個(gè)接口類(lèi)型的變量,將不同對(duì)象實(shí)例的引用(地址)賦給它,運(yùn)行時(shí)就會(huì)動(dòng)態(tài)綁定到相應(yīng)的類(lèi)上,執(zhí)行其所實(shí)現(xiàn)的方法,如下:var變量名接口名變量名=&類(lèi)名1{...} //給變量賦第1個(gè)類(lèi)的對(duì)象實(shí)例變量名.方法名i() //執(zhí)行第1個(gè)類(lèi)所實(shí)現(xiàn)的方法i變量名=&類(lèi)名2{...} //給變量賦第2個(gè)類(lèi)的對(duì)象實(shí)例變量名.方法名i() //執(zhí)行第2個(gè)類(lèi)所實(shí)現(xiàn)的方法i多態(tài)的實(shí)現(xiàn)【實(shí)例3.3】運(yùn)用多態(tài)分別判斷一個(gè)人和一只大熊貓是否成年。背景知識(shí):對(duì)于一個(gè)人而言,滿18周歲才算成年,熊貓則不然,由于熊貓的生理發(fā)育進(jìn)程比人類(lèi)快得多(大約在3倍以上),四五歲就已性成熟,而一般超過(guò)20歲的大熊貓就被認(rèn)為是老年熊貓。大熊貓的年齡和人類(lèi)的年齡可以用以下公式來(lái)大致?lián)Q算:

大熊貓年齡≈3.5×人類(lèi)年齡+1.5實(shí)現(xiàn)思路:本例先定義一個(gè)父類(lèi)Animal(動(dòng)物類(lèi)),再定義兩個(gè)子類(lèi)Human(人類(lèi))和Panda(熊貓類(lèi))繼承自Animal類(lèi)。(1)定義一個(gè)Adult(成年)接口,其中有獲取姓名和年齡的getName和getAge方法,獲取物種名的getSpecies方法,還有判斷對(duì)象是否成年的isAdult方法。(2)由父類(lèi)Animal實(shí)現(xiàn)接口的getName和getAge方法,這兩個(gè)方法是公共的,僅有唯一的實(shí)現(xiàn),Human和Panda類(lèi)都可以繼承使用。多態(tài)的實(shí)現(xiàn)(3)Human和Panda類(lèi)分別實(shí)現(xiàn)各自的getSpecies和isAdult方法,獲取自身所屬的物種名,并以不同的算法來(lái)判斷自己的類(lèi)對(duì)象是否成年,實(shí)現(xiàn)多態(tài)。程序代碼如下(adult.go):運(yùn)行結(jié)果如圖。第3章Go語(yǔ)言面向?qū)ο缶幊填?lèi)與方法01用結(jié)構(gòu)體定義類(lèi)1.命名與未命名類(lèi)型2.自定義命名類(lèi)型3.基于結(jié)構(gòu)體定義的類(lèi)4.基于類(lèi)定義的類(lèi)用結(jié)構(gòu)體定義類(lèi)1.命名與未命名類(lèi)型在Go語(yǔ)言中,數(shù)據(jù)類(lèi)型分為命名與未命名兩種。(1)命名類(lèi)型所有基本數(shù)據(jù)類(lèi)型,包括整型、浮點(diǎn)型、復(fù)數(shù)型、布爾型、字符串型等都是命名類(lèi)型,之所以叫“命名類(lèi)型”是因?yàn)椋緮?shù)據(jù)類(lèi)型的保留字就唯一確定了這個(gè)類(lèi)型本身,比如,兩個(gè)整型變量“varaint”與“varbint”,它們的類(lèi)型完全相同并無(wú)任何差異,其保留字名int也就是類(lèi)型名。除了Go內(nèi)置的基本數(shù)據(jù)類(lèi)型之外,用戶自定義的類(lèi)型也是命名類(lèi)型,例如,自定義的類(lèi):typeHumanstruct{ ...}(2)未命名類(lèi)型反之,那些沒(méi)有固定名稱來(lái)唯一確定其類(lèi)型的則是未命名類(lèi)型。用結(jié)構(gòu)體定義類(lèi)2.自定義命名類(lèi)型Go的對(duì)象系統(tǒng)是基于用戶自定義類(lèi)型構(gòu)建起來(lái)的,自定義類(lèi)型使用關(guān)鍵字type,基本語(yǔ)法格式為:type類(lèi)型名已有類(lèi)型其中,“類(lèi)型名”是自定義類(lèi)型的名稱,由用戶任取,只要符合Go的標(biāo)識(shí)符命名規(guī)范就行;“已有類(lèi)型”可以是Go語(yǔ)言的基本數(shù)據(jù)類(lèi)型、任何型態(tài)的復(fù)合數(shù)據(jù)類(lèi)型,當(dāng)然也可以是另一個(gè)自定義類(lèi)型。顯然,自定義類(lèi)型有其確定的類(lèi)型名,是“命名類(lèi)型”。例如:typeMyFloatfloat32 //基于基本數(shù)據(jù)類(lèi)型float32定義的類(lèi)型typeINTint //基于基本數(shù)據(jù)類(lèi)型int定義的類(lèi)型typep_INT*int //基于整型指針定義的類(lèi)型types_INT[]int //基于整型切片定義的類(lèi)型typecircleAreafunc(float32)float32 //基于函數(shù)(函數(shù)簽名)定義的類(lèi)型用結(jié)構(gòu)體定義類(lèi)3.基于結(jié)構(gòu)體定義的類(lèi)Go語(yǔ)言的結(jié)構(gòu)體(struct)是一種由一系列相同或不同類(lèi)型的數(shù)據(jù)組合而成的集合,其中每個(gè)數(shù)據(jù)項(xiàng)稱為該結(jié)構(gòu)體的“成員”,各成員可以是基本類(lèi)型也可以是復(fù)合類(lèi)型的數(shù)據(jù),甚至還可以是另一個(gè)結(jié)構(gòu)體。顯然,結(jié)構(gòu)體是一種“未命名”的復(fù)合數(shù)據(jù)類(lèi)型,可以基于它用以上方法定義出一個(gè)個(gè)命名的數(shù)據(jù)類(lèi)型來(lái),形式如下:type類(lèi)型名struct{

字段1類(lèi)型1

字段2類(lèi)型2 ...

字段n類(lèi)型n}用結(jié)構(gòu)體定義類(lèi)其中,結(jié)構(gòu)體的每個(gè)成員對(duì)應(yīng)于所定義類(lèi)型中的一個(gè)字段,每個(gè)字段都擁有自己的類(lèi)型,且同一個(gè)結(jié)構(gòu)體類(lèi)型定義中不能有相同的字段名,字段的類(lèi)型可以相同也可以不同,如果幾個(gè)字段的類(lèi)型相同,也可以將它們寫(xiě)在同一行,形如:type類(lèi)型名struct{

字段1類(lèi)型1 ...

字段i,字段i+1,...,字段i+k類(lèi)型i ...}將以上定義中的“類(lèi)型名”作為“類(lèi)名”、“字段”作為“屬性”,就得到了之前所講的面向?qū)ο蠓庋b中類(lèi)的定義:type類(lèi)名struct{

屬性1類(lèi)型1

屬性2類(lèi)型2 ...

屬性n類(lèi)型n}用結(jié)構(gòu)體定義類(lèi)4.基于類(lèi)定義的類(lèi)既然類(lèi)本質(zhì)上是一種類(lèi)型(自定義的命名類(lèi)型),當(dāng)然也可以基于已有類(lèi)定義出新類(lèi),使用語(yǔ)句:type新類(lèi)名已有類(lèi)名但要注意:用這種方式定義的類(lèi)是一個(gè)新的命名類(lèi)型,它與原有類(lèi)之間并不存在繼承關(guān)系,也不會(huì)繼承原有類(lèi)的方法?!緦?shí)例3.4】證明“白馬非馬”。背景知識(shí):“白馬非馬”是戰(zhàn)國(guó)時(shí)思想家、名家代表人物公孫龍(前320~前250年)所提出的一個(gè)著名的命題。相傳有一次公孫龍牽一匹白馬出關(guān),被守關(guān)士兵攔下告知“國(guó)君有令嚴(yán)禁馬匹出關(guān)!”公孫龍說(shuō)“國(guó)君只說(shuō)不讓馬出關(guān),而我牽的這匹是白馬,白馬與馬是不同的……”一番長(zhǎng)篇大論,有理有據(jù),駁得士兵啞口無(wú)言,只得放行。

——詳見(jiàn)《公孫龍子·白馬論》用結(jié)構(gòu)體定義類(lèi)讀者可能會(huì)奇怪:白馬難道不是馬么?公孫龍究竟是用什么樣的理由說(shuō)服守關(guān)的士兵放行的,看了下面的程序大家就會(huì)明白其中道理了。程序代碼如下(horse.go):說(shuō)明:(a)語(yǔ)句fmt.Println(whitehorse.isHorse())執(zhí)行輸出錯(cuò)誤信息:whitehorse.isHorseundefined(typeWhiteHorsehasnofieldormethodisHorse)。這說(shuō)明白馬確實(shí)“非馬”。(b)語(yǔ)句fmt.Println(yellowhorse.isHorse())執(zhí)行輸出錯(cuò)誤信息:yellowhorse.isHorseundefined(typeYellowHorsehasnofieldormethodisHorse)。黃馬也“非馬”。在注釋掉以上兩句代碼后,運(yùn)行結(jié)果如圖。02類(lèi)的初始化1.按屬性順序2.指定屬性名3.使用new函數(shù)類(lèi)的初始化1.按屬性順序此方式將類(lèi)的各個(gè)屬性值按照定義時(shí)所聲明的順序依次羅列在一對(duì)大括號(hào)“{}”內(nèi),有3種不同寫(xiě)法,如下://寫(xiě)法1h1:=Human{"王林",1.75,65,19}

//寫(xiě)法2h2:=Human{ "Tom", 1.83, 81.5, 18, //加上英文逗號(hào)}

//寫(xiě)法3h3:=Human{ "王燕", 1.66, 49, 20}類(lèi)的初始化2.指定屬性名此方式顯式地指定需要初始化的屬性名及其值(中間以冒號(hào)“:”分隔),也有3種寫(xiě)法://寫(xiě)法1h1:=Human{name:"王林",height:1.75,weight:65,age:19}

//寫(xiě)法2h2:=Human{ name:"Tom", height:1.83, weight:81.5, age:18, //加上英文逗號(hào)}

//寫(xiě)法3h3:=Human{ name:"王燕", height:1.66, weight:49, age:20}同樣地,如果結(jié)尾的“}”獨(dú)占一行,最后的屬性值后面也要加逗號(hào)。類(lèi)的初始化這種方式的好處是寫(xiě)法比較靈活,可以不必嚴(yán)格按照類(lèi)定義的屬性順序賦值,也可以省略一些屬性(由系統(tǒng)自動(dòng)初始化為其類(lèi)型的零值),另外,當(dāng)修改了類(lèi)的定義(如增加屬性)時(shí),只須對(duì)增加的屬性單獨(dú)賦值,而原來(lái)的初始化代碼段不用做任何修改,例如:3.使用new函數(shù)new是Go系統(tǒng)的內(nèi)置函數(shù),它一次性將類(lèi)的所有屬性都初始化為各自類(lèi)型的零值,并返回一個(gè)指向類(lèi)體(結(jié)構(gòu)體)的指針,如下:h3:=new(Human)fmt.Println(h3) //&{000}03類(lèi)的方法1.方法的本質(zhì)2.方法調(diào)用類(lèi)的方法1.方法的本質(zhì)方法是一種對(duì)類(lèi)行為的封裝,將3.2.1節(jié)方法定義與2.7.1節(jié)函數(shù)定義的語(yǔ)法格式放在一起加以比較,如下。方法定義:func(指針名*類(lèi)名)方法名([形參列表])[(返回值列表)]{

方法體 [return[值列表]]}函數(shù)定義:func函數(shù)名([參數(shù)列表])[(返回值列表)]{

函數(shù)體 [return[值列表]]}類(lèi)的方法如果將方法定義語(yǔ)法中的“方法名”看作是一個(gè)特殊的參數(shù)(其類(lèi)型為指向類(lèi)名的指針)并與后面的“形參列表”合并為一個(gè)完整的“參數(shù)列表”,那么方法本質(zhì)上其實(shí)就是一個(gè)函數(shù),只不過(guò)它顯式地指定將類(lèi)對(duì)象的指針作為自己的第一個(gè)參數(shù)而已,這個(gè)參數(shù)在Go語(yǔ)言中又被稱為方法的“接收者”,這樣一來(lái),方法定義的語(yǔ)法也就可以改寫(xiě)為函數(shù)定義的形式,如下:func函數(shù)名(接收者名接收類(lèi)型[,其他參數(shù)列表])[(返回值列表)]{

函數(shù)(方法)體 [return[值列表]]}類(lèi)的方法【實(shí)例3.5】將前面【實(shí)例3.1】人類(lèi)Human計(jì)算BMI的方法改寫(xiě)為一個(gè)函數(shù),實(shí)現(xiàn)同樣的計(jì)算功能。程序代碼如下(method01.go):運(yùn)行結(jié)果如圖。方法的“接收者”除了是指針類(lèi)型外,還可以是值類(lèi)型,如將上例中計(jì)算BMI的函數(shù)寫(xiě)成如下:funcbmiCal(hHuman)float32{ //接收者為值類(lèi)型 returnh.weight/(h.height*h.height)}然后在調(diào)用函數(shù)時(shí)將傳入的實(shí)參由引用改為類(lèi)對(duì)象的變量值:fmt.Println(man.getName(),"BMI指數(shù)是",bmiCal(man))類(lèi)的方法2.方法調(diào)用可通過(guò)兩種方式調(diào)用類(lèi)的方法,如下。(1)方法值調(diào)用這是最普通的方式,調(diào)用格式為:對(duì)象名.方法名([實(shí)參列表])或者(對(duì)象名).方法名([實(shí)參列表])值調(diào)用返回的是一個(gè)值類(lèi)型的變量,可以直接在程序中使用,如打印輸出或賦值給其他變量。類(lèi)的方法(2)方法表達(dá)式這種方式實(shí)際是將類(lèi)的方法轉(zhuǎn)換為函數(shù),再通過(guò)調(diào)用函數(shù)來(lái)執(zhí)行方法。剛剛已揭示了方法的本質(zhì)其實(shí)就是函數(shù),既然如此,可以用“類(lèi)名”與“方法名”所構(gòu)成的表達(dá)式來(lái)唯一地確定一個(gè)等價(jià)函數(shù),這個(gè)表達(dá)式又稱為“方法表達(dá)式”。根據(jù)方法“接收者”類(lèi)型的不同,方法表達(dá)式有兩種書(shū)寫(xiě)格式,如下。①當(dāng)“接收者”為值類(lèi)型時(shí),方法表達(dá)式寫(xiě)為:類(lèi)名.方法名②當(dāng)“接收者”為指針類(lèi)型時(shí),方法表達(dá)式寫(xiě)為:(*類(lèi)名).方法名 //注意這里的括號(hào)不能省略類(lèi)的方法【實(shí)例3.6】分別用上述兩種方式調(diào)用方法,計(jì)算圓面積和周長(zhǎng)。程序代碼如下(method02.go):運(yùn)行結(jié)果如圖。04類(lèi)的嵌套和方法覆蓋1.內(nèi)嵌屬性的訪問(wèn)2.內(nèi)嵌方法的覆蓋類(lèi)的嵌套和方法覆蓋1.內(nèi)嵌屬性的訪問(wèn)用點(diǎn)操作符“.”訪問(wèn)內(nèi)嵌屬性,當(dāng)有n層類(lèi)的嵌套時(shí),可使用全路徑進(jìn)行訪問(wèn),形如“對(duì)象名.類(lèi)名1.類(lèi)名2....類(lèi)名n.屬性”,但是,如果屬性在其整個(gè)路徑的嵌套結(jié)構(gòu)中是唯一的,就不需要寫(xiě)出全路徑,簡(jiǎn)寫(xiě)為“對(duì)象名.屬性”即可。例如,定義A、B、C三個(gè)類(lèi):typeAstruct{ astring bstring}

typeBstruct{ A bstring}

typeCstruct{ B bstring cstring}類(lèi)的嵌套和方法覆蓋分別創(chuàng)建它們的對(duì)象實(shí)例并初始化如下:objA:=A{ a:"I'mA.", b:"It'sA'sb."}objB:=B{ A:objA, b:"I'mB."}objC:=C{ B:objB, b:"It'sC'sb.", c:"I'mC.",}類(lèi)的嵌套和方法覆蓋由于只有基類(lèi)A具有a屬性,故objC.a、objC.B.a、objC.B.A.a、objB.A.a、objB.a都指的是A類(lèi)的a,輸出測(cè)試如下:fmt.Println(objC.a) //I'mA.fmt.Println(objC.B.a) //I'mA.fmt.Println(objC.B.A.a) //I'mA.fmt.Println(objB.A.a) //I'mA.fmt.Println(objB.a) //I'mA.但是,因?yàn)檫@三個(gè)類(lèi)皆有b屬性,所以objC.b、objC.B.b、objC.B.A.b是不同類(lèi)的b,訪問(wèn)時(shí)必須寫(xiě)出全路徑而不能簡(jiǎn)寫(xiě),輸出測(cè)試如下:fmt.Println(objC.a) //I'mA.fmt.Println(objC.B.a) //I'mA.fmt.Println(objC.B.A.a) //I'mA.fmt.Println(objB.A.a) //I'mA.fmt.Println(objB.a) //I'mA.fmt.Println(objC.b) //It'sC'sb.fmt.Println(objC.B.b) //I'mB.fmt.Println(objC.B.A.b) //It'sA'sb.類(lèi)的嵌套和方法覆蓋2.內(nèi)嵌方法的覆蓋內(nèi)嵌方法調(diào)用也使用點(diǎn)操作符“.”,外層對(duì)象調(diào)用內(nèi)嵌類(lèi)的方法時(shí)也可以像訪問(wèn)內(nèi)嵌屬性一樣使用全路徑或簡(jiǎn)寫(xiě),當(dāng)采用簡(jiǎn)寫(xiě)形式時(shí),Go編譯器會(huì)從外向內(nèi)逐層查找,如果外層類(lèi)與內(nèi)嵌類(lèi)有相同的方法,優(yōu)先調(diào)用最外層的方法,從而實(shí)現(xiàn)子類(lèi)方法對(duì)父類(lèi)方法的覆蓋。比如,對(duì)上面的A、B、C三個(gè)類(lèi),分別定義如下同名的方法:func(tA)say(){ //A類(lèi)的say方法 fmt.Println("Hi!",t.a)}

func(tB)say(){ //B類(lèi)的say方法 fmt.Println("Hi!",t.b)}

func(tC)say(){ //C類(lèi)的say方法 fmt.Println("Hi!",t.c)}類(lèi)的嵌套和方法覆蓋在程序中用不同方式調(diào)用它們,測(cè)試如下://從外向內(nèi)查找,首先找到的是C類(lèi)的say方法objC.say() //Hi!I'mC.//自B類(lèi)對(duì)象開(kāi)始向內(nèi)查找,優(yōu)先執(zhí)行B類(lèi)的say方法objB.say() //Hi!I'mB.//用全路徑調(diào)用最內(nèi)層A類(lèi)的say方法objC.B.A.say() //Hi!I'mA.類(lèi)的嵌套和方法覆蓋使得Go語(yǔ)言的類(lèi)具備了強(qiáng)大的表達(dá)力,幾乎可以表示客觀世界中任何復(fù)雜的對(duì)象實(shí)體,并給對(duì)象擴(kuò)充出豐富的行為能力,下面通過(guò)一個(gè)實(shí)例形象地演示這一點(diǎn)。類(lèi)的嵌套和方法覆蓋【實(shí)例3.7】演示從“魚(yú)”到“人”的進(jìn)化。背景知識(shí):根據(jù)古生物學(xué)的研究,生物的進(jìn)化經(jīng)歷了從水生到陸生的演變過(guò)程。最初地球上的生物都生活在海洋中,是魚(yú)類(lèi)的時(shí)代。大約3億7500萬(wàn)年前(泥盆紀(jì)晚期)的某個(gè)時(shí)候,有一種提塔利克魚(yú)(學(xué)名:Tiktaalik,見(jiàn)圖)的鰭發(fā)生了變異,長(zhǎng)出原始的腕骨及趾頭,于是這些魚(yú)紛紛嘗試著用鰭支撐身體爬上岸來(lái),具有了初步的陸上爬行能力,演化出包括恐龍、猿猴在內(nèi)種類(lèi)繁多的陸地動(dòng)物。到了距今550萬(wàn)年前(中新世末期),生活在非洲大陸東南部的一群猿猴(南方古猿)由于氣候環(huán)境變化被迫下到地面,逐步學(xué)會(huì)了直立行走,最終進(jìn)化成人類(lèi)。類(lèi)的嵌套和方法覆蓋實(shí)現(xiàn)思路:本例先定義一個(gè)基類(lèi)Fish(魚(yú)類(lèi)),及兩個(gè)表示能力的類(lèi)SwimAbility(游泳)和WalkAbility(行走)。Fish類(lèi)嵌套SwimAbility表示魚(yú)會(huì)游泳,并實(shí)現(xiàn)一個(gè)基礎(chǔ)的游泳方法swimming;再定義一個(gè)Tiktaalik(提塔利克魚(yú)類(lèi))繼承自Fish類(lèi),增加嵌套一個(gè)WalkAbility類(lèi)表示它比一般的魚(yú)多了行走能力,并實(shí)現(xiàn)基礎(chǔ)的行走方法walking;然后定義Tiktaalik的子類(lèi)Monkey(猿猴類(lèi)),覆蓋其父類(lèi)的walking方法,以直立行走取代爬行;最后定義的Human(人類(lèi))繼承Monkey類(lèi),就同時(shí)擁有了魚(yú)和猿的能力。另外,還定義了一個(gè)Sailfish(旗魚(yú)類(lèi)),它直接派生自Fish,并沒(méi)有嵌套新的能力類(lèi),但重寫(xiě)(覆蓋)了Fish類(lèi)的swimming方法,游泳能力極大地增強(qiáng)了。程序代碼如下(fishtohuman.go):運(yùn)行結(jié)果如圖。第3章Go語(yǔ)言面向?qū)ο缶幊探涌?1接口聲明與初始化1.接口聲明2.接口初始化3.空接口接口聲明與初始化1.接口聲明接口在本質(zhì)上是一種類(lèi)型,故也像其他自定義類(lèi)型一樣用type關(guān)鍵字聲明。接口是一組方法的集合(也可以只有一個(gè)方法),但不包含這些方法的具體實(shí)現(xiàn),其聲明的語(yǔ)法格式如下:type接口名interface{

方法1([形參列表])[(返回值列表)]

方法2([形參列表])[(返回值列表)] ...

方法n([形參列表])[(返回值列表)]}例如:typeAquaticAnimalinterface{ //水生動(dòng)物接口 swimming()string //游泳方法}

typeLandAnimalinterface{ //陸地動(dòng)物接口 swimming()string //游泳方法 walking()string //行走方法}

typeManKindinterface{ //人類(lèi)接口 swimming()string //游泳方法 walking()string //行走方法 manufacturing(toolstring)string //制造工具方法}接口聲明與初始化接口聲明中除了單純的方法,還可以嵌入其他接口,這一點(diǎn)與類(lèi)的嵌套(繼承)有相似之處,如上面人類(lèi)接口的定義還可以寫(xiě)成:typeManKindinterface{ AquaticAnimal //嵌入水生動(dòng)物接口 walking()string manufacturing(toolstring)string}或者:typeManKindinterface{ LandAnimal //嵌入陸地動(dòng)物接口 manufacturing(toolstring)string}接口聲明與初始化2.接口初始化接口初始化的方式有兩種,如下。1)用對(duì)象實(shí)例賦值如果一個(gè)類(lèi)實(shí)現(xiàn)了接口中的所有方法(實(shí)現(xiàn)了該接口),就可以把這個(gè)類(lèi)的對(duì)象實(shí)例賦值給接口。例如,定義一個(gè)人類(lèi)Human,并實(shí)現(xiàn)人類(lèi)接口中的所有(3個(gè))方法,如下:typeHumanstruct{ //人類(lèi) namestring}

func(h*Human)swimming()string{ //實(shí)現(xiàn)游泳方法 return"會(huì)游泳"}

func(h*Human)walking()string{ //實(shí)現(xiàn)行走方法 return"會(huì)行走"}

func(h*Human)manufacturing(toolstring)string{ //實(shí)現(xiàn)制造工具方法 return"會(huì)制造"+tool}接口聲明與初始化人類(lèi)實(shí)現(xiàn)了ManKind接口,就可以將人類(lèi)的對(duì)象實(shí)例賦值給ManKind接口,如下:funcmain(){ man:=Human{"北京猿人"} //創(chuàng)建一個(gè)人類(lèi)的對(duì)象實(shí)例 varmanKindManKind=&man //賦值給接口 fmt.Println("我是",,",",manKind.manufacturing("石器"),"。")} //我是北京猿人,會(huì)制造石器。說(shuō)明:(1)在賦值給接口時(shí)使用的是對(duì)象實(shí)例man的引用(&),因?yàn)镚o語(yǔ)言的接口本質(zhì)上是一個(gè)指針類(lèi)型。(2)雖然對(duì)象實(shí)例賦給了接口,但只能通過(guò)這個(gè)接口調(diào)用對(duì)象實(shí)例的方法而不能訪問(wèn)其屬性,要獲取對(duì)象屬性,只能用“對(duì)象.getXxx()”(若有g(shù)et方法)或“對(duì)象.屬性名”,而不能用“接口.屬性名”,例如,如果將最后的輸出語(yǔ)句改為:fmt.Println("我是",manK,",",manKind.manufacturing("石器"),"。")運(yùn)行會(huì)報(bào)錯(cuò):manKundefined(typeManKindhasnofieldormethodname)(3)只要接口的方法集是對(duì)象實(shí)例方法集的子集就都可以實(shí)現(xiàn)這種賦值,例如,人類(lèi)對(duì)象也同樣可以賦給陸地動(dòng)物和水生動(dòng)物接口,如下:varlandAnimalLandAnimal=&man //賦給陸地動(dòng)物接口fmt.Println("我是",,",",landAnimal.walking(),"。") //我是北京猿人,會(huì)行走。varaquaticAnimalAquaticAnimal=&man //賦給水生動(dòng)物接口fmt.Println("我是",,",",aquaticAnimal.swimming(),"。") //我是北京猿人,會(huì)游泳。接口聲明與初始化2)用接口變量賦值用一個(gè)已經(jīng)初始化的接口類(lèi)型變量賦給另一個(gè)接口,這種方式在以下幾種情況下可以。(1)兩個(gè)接口等價(jià)兩個(gè)擁有完全相同的方法集的接口稱為等價(jià)接口,這個(gè)很好理解,如果接口A和接口B的方法是相同的,那么一個(gè)類(lèi)只要實(shí)現(xiàn)了接口A自然也就實(shí)現(xiàn)了接口B,反之亦然,A和B實(shí)際上是等同的,它們的變量可以相互賦值。由于接口聲明對(duì)方法的先后順序并無(wú)要求,所以只要兩個(gè)接口包含一樣的方法,它們就滿足等價(jià)條件,如下面這兩個(gè)接口:typeOldManinterface{ //古人接口 swimming()string walking()string manufacturing(toolstring)string}

typeNewManinterface{ //新人接口 manufacturing(toolstring)string walking()string swimming()string}接口聲明與初始化它們包含的都是swimming(游泳)、walking(行走)和manufacturing(制造工具)這3個(gè)方法,雖然方法聲明的順序不一致,接口的名稱也不一樣,但這兩個(gè)接口實(shí)質(zhì)是相同的,即它們完全等價(jià),于是可以編寫(xiě)語(yǔ)句相互賦值,如下:man1:=Human{"北京猿人"}varoldManOldMan=&man1varnewManNewMan=oldMan //古人接口的變量賦給新人接口fmt.Println("我是",,",",newMan.manufacturing("舊石器"),"。") //我是北京猿人,會(huì)制造舊石器。man2:=Human{"山頂洞人"}newMan=&man2oldMan=newMan //新人接口的變量賦給古人接口fmt.Println("我是",,",",oldMan.manufacturing("新石器"),"。") //我是山頂洞人,會(huì)制造新石器。接口聲明與初始化(2)被賦值接口的方法集是對(duì)方的子集如果接口A的方法集中包含了接口B的所有方法,即B的方法集是A的方法集的子集,就可以將接口A的變量賦值給B,但反之不行。例如:typeLandAnimalinterface{ //陸地動(dòng)物(對(duì)應(yīng)上面所說(shuō)接口A) swimming()string walking()string}

typeAquaticAnimalinterface{ //水生動(dòng)物(對(duì)應(yīng)上面所說(shuō)接口B) swimming()string}接口聲明與初始化陸地動(dòng)物接口的方法集有swimming(游泳)和walking(行走)兩個(gè)方法,它包含了水生動(dòng)物接口僅有的一個(gè)swimming方法,故陸地動(dòng)物接口的變量可以賦給水生動(dòng)物接口,例如:typeFishstruct{ //魚(yú)類(lèi) categorystring}func(f*Fish)swimming()string{ //實(shí)現(xiàn)水生動(dòng)物接口 return"會(huì)游泳"}

typeTiktaalikstruct{ //提塔利克魚(yú) Fish}func(t*Tiktaalik)walking()string{ //實(shí)現(xiàn)陸地動(dòng)物接口 return"會(huì)爬行"}

funcmain(){ fish1:=Fish{"魚(yú)"} fish2:=Tiktaalik{Fish{"提塔利克魚(yú)"}} varnewAnimalLandAnimal=&fish2 varoldAnimalAquaticAnimal=newAnimal //陸地動(dòng)物接口變量賦給水生動(dòng)物 fmt.Println(fish2.category,"是",fish1.category,",",oldAnimal.swimming(),"。") //提塔利克魚(yú)是魚(yú),會(huì)游泳。}接口聲明與初始化但是,反過(guò)來(lái)卻不行,如下語(yǔ)句:oldAnimal=&fish1newAnimal=oldAnimal //水生動(dòng)物接口變量賦給陸地動(dòng)物fmt.Println(fish1.category,"是",fish2.category,",",newAnimal.walking(),"。")運(yùn)行會(huì)報(bào)錯(cuò):cannotuseoldAnimal(variableoftypeAquaticAnimal)asLandAnimalvalueinassignment:AquaticAnimaldoesnotimplementLandAnimal(missingmethodwalking)接口聲明與初始化3.空接口1)空接口的賦值顯然,空接口的方法集為空,所以任何類(lèi)(包括數(shù)據(jù)類(lèi)型)都被認(rèn)為實(shí)現(xiàn)了空接口,任何類(lèi)(類(lèi)型)的實(shí)例都可以賦值給空接口。例如:2)空接口的比較在Go中用一個(gè)內(nèi)部常量nil代表空值,它也是空接口的初始值,但是,一個(gè)空接口類(lèi)型的變量并非在任何情況下都是空的,兩個(gè)空接口也不總是相等。接口聲明與初始化下面聲明兩個(gè)空接口nulFace1和nulFace2,用程序測(cè)試并比較它們的實(shí)際值。varnulFace1,nulFace2interface{} //聲明兩個(gè)空接口(1)所有空接口變量初始值都為nil,都相等。測(cè)試如下:fmt.Println(nulFace1,nulFace2) //<nil><nil>fmt.Println(nulFace1==nil) //truefmt.Println(nulFace1==nulFace2) //true(2)任何非空接口變量的默認(rèn)值也為nil,與空接口相等。例如,對(duì)于尚未初始化的水生動(dòng)物接口:typeAquaticAnimalinterface{ swimming()string}執(zhí)行語(yǔ)句:varoldAnimalAquaticAnimal //聲明接口變量(但不初始化)fmt.Println(oldAnimal) //<nil>fmt.Println(nulFace1==oldAnimal) //true接口聲明與初始化(3)當(dāng)一個(gè)空接口接受了賦值之后,其值會(huì)出現(xiàn)多種情形,如下。①如果給空接口賦值的是一個(gè)有值的變量(或常量),則賦值后空接口值就等于該變量(或常量)的值,不再為nil,也與其他空接口不再相等。例如:constcint=299792458 //整型常量nulFace1=c //常量賦給空接口fmt.Println(nulFace1) //299792458fmt.Println(nulFace1==c) //truefmt.Println(nulFace1==nil) //falsefmt.Println(nulFace1==nulFace2) //falsenulFace1=nil //將接口nulFace1重置為空②如果給空接口賦值的是一個(gè)已經(jīng)初始化了的非空接口,則賦值后空接口就與該非空接口相等,不再為nil,也與其他空接口不再相等。例如,將水生動(dòng)物接口初始化后賦給空接口:fish1:=Fish{"魚(yú)"}oldAnimal=&fish1 //初始化水生動(dòng)物接口nulFace1=oldAnimal //賦給空接口fmt.Println(nulFace1) //&{魚(yú)}fmt.Println(nulFace1==oldAnimal) //truefmt.Println(nulFace1==nil) //falsefmt.Println(nulFace1==nulFace2) //false接口聲明與初始化③如果給空接口賦值的是一個(gè)尚未初始化的非空接口,則賦值后空接口值為(回歸)空。例如,對(duì)于尚未初始化的陸地動(dòng)物接口:typeLandAnimalinterface{ swimming()string walking()string}執(zhí)行語(yǔ)句:varlandAnimalLandAnimal //聲明接口變量(但未初始化)nulFace1=landAnimal //賦給空接口fmt.Println(nulFace1==nil) //truefmt.Println(nulFace1==nulFace2) //true接口聲明與初始化④如果給空接口賦值的是未初始化的變量,則賦值后空接口值為該變量類(lèi)型的零值或空值,不再為nil,也與其他空接口不再相等。例如:varaintnulFace1=a //未初始化的整型變量賦給空接口fmt.Println(nulFace1) //0fmt.Println(nulFace1==nil) //falsefmt.Println(nulFace1==nulFace2) //falsevarbcomplex64nulFace1=b //未初始化復(fù)數(shù)型變量賦給空接口fmt.Println(nulFace1) //(0+0i)接口聲明與初始化(4)兩個(gè)分別被賦了變量值的空接口的相等性取決于賦給它們的變量的值是否相等。例如:vard1float32=3.14vard2float32=math.Pi //圓周率精確值nulFace1=d1nulFace2=d2fmt.Println(nulFace1==nulFace2) //falsenulFace2=d1fmt.Println(nulFace1==nulFace2) //true02接口類(lèi)型推斷1.類(lèi)型判斷2.類(lèi)型查詢接口類(lèi)型推斷1.類(lèi)型判斷類(lèi)型判斷又稱“類(lèi)型斷言”,寫(xiě)為一個(gè)表達(dá)式:接口變量名.(類(lèi)型名)它用于判斷接口變量綁定的實(shí)例是否實(shí)現(xiàn)了(或本身就是)括號(hào)中所指類(lèi)型的接口(或數(shù)據(jù)),故這里的“類(lèi)型名”可以是一個(gè)接口類(lèi)型名,也可以是其他(基本或復(fù)合)數(shù)據(jù)類(lèi)型名。程序中的類(lèi)型判斷表達(dá)式通常寫(xiě)在如下形式的代碼塊中:if_,ok:=類(lèi)型判斷表達(dá)式;ok{ ...//代碼段1}else{ ...//代碼段2}接口類(lèi)型推斷【實(shí)例3.8】判斷一種類(lèi)人動(dòng)物是否是“人”。背景知識(shí):在人類(lèi)從“猿”到“人”的進(jìn)化過(guò)程中,產(chǎn)生了很多中間過(guò)渡類(lèi)型的動(dòng)物,古人類(lèi)學(xué)家判斷一種動(dòng)物是否是真正意義上的“人”,主要依據(jù)兩條標(biāo)準(zhǔn)——直立行走和制造工具。如果一種類(lèi)人動(dòng)物已經(jīng)會(huì)直立行走了,但還不能制造工具,那么它只能被劃歸為類(lèi)人猿亞目下的“人科”成員,而不是“人屬”物種,即還不能算作人,例如,史上曾經(jīng)出現(xiàn)過(guò)的乍得沙赫人、始祖地猿、南方古猿、鮑氏傍人……雖然它們有的名稱中也帶有“人”字,但本質(zhì)上并不是人;只有會(huì)制造工具的物種,如元謀人、藍(lán)田人、北京猿人、山頂洞人、尼安德特人等,才是“人屬”動(dòng)物,才算真正的“人”。實(shí)現(xiàn)思路:本例先定義一個(gè)Monkey(猿猴)接口,里面有walking(直立行走)方法;定義ManKind(人類(lèi))接口嵌套(繼承)Monkey接口,再增加一個(gè)manufacturing(制造工具)方法。Ape(類(lèi)人猿)類(lèi)實(shí)現(xiàn)Monkey接口,Human(人)類(lèi)繼承Ape類(lèi),并進(jìn)一步實(shí)現(xiàn)ManKind接口。程序根據(jù)運(yùn)行時(shí)接口的實(shí)際類(lèi)型來(lái)判斷一個(gè)對(duì)象究竟是猿還是人。程序代碼如下(ishuman.go):運(yùn)行結(jié)果如圖。接口類(lèi)型推斷2.類(lèi)型查詢?nèi)绻粋€(gè)接口變量的類(lèi)型尚不確定(或有多種可能),可使用“接口變量名.(type)”表達(dá)式結(jié)合switch語(yǔ)句進(jìn)行類(lèi)型查詢以確定變量所屬的準(zhǔn)確類(lèi)型,語(yǔ)法格式如下:switch接口變量名.(type){case類(lèi)型名1: [<語(yǔ)句1>]case類(lèi)型名2: [<語(yǔ)句2>]...case類(lèi)型名n: [<語(yǔ)句n>][default:<語(yǔ)句n+1>]}接口類(lèi)型推斷【實(shí)例3.9】查詢確定動(dòng)物的類(lèi)型。程序代碼如下(iswhatanimal.go):運(yùn)行結(jié)果如圖。接口類(lèi)型推斷需要特別注意以下兩點(diǎn):(1)fallthrough語(yǔ)句不能用在類(lèi)型查詢的switch語(yǔ)句中。(2)當(dāng)查詢的多個(gè)可能類(lèi)型之間存在嵌套(繼承)關(guān)系,如本例的三個(gè)接口之間是逐層嵌套定義的,要將子接口的判斷分支寫(xiě)在父接口之前,因?yàn)槌绦蛑灰ヅ渖弦粋€(gè)分支,就不會(huì)再去比對(duì)其余的分支,若將上面的查詢函數(shù)改成如下:funcisWhatAnimal(animalAquaticAnimal){ switchanimal.(type){ caseAquaticAnimal: fmt.Println(animal.getName(),animal.swimming(),",是水生動(dòng)物。") caseLandAnimal: fmt.Println(animal.getName(),animal.swimming(),",是陸地動(dòng)物。") caseManKind: fmt.Println(animal.getName(),animal.swimming(),",是人。") default: fmt.Println(animal.getName(),"所屬動(dòng)物類(lèi)型不確定!") }}接口類(lèi)型推斷再執(zhí)行語(yǔ)句:varanimalAquaticAnimalanimal=&Human{Ape{Fish{"周何駿"}}}isWhatAnimal(animal)就會(huì)得出一個(gè)人是水生動(dòng)物的結(jié)論如圖,這顯然是荒謬的!第3章Go語(yǔ)言面向?qū)ο缶幊谭?/p>

射反

射Go語(yǔ)言提供一種機(jī)制在運(yùn)行時(shí)更新變量和檢查它們的值、調(diào)用它們的方法,但是在編譯時(shí)并不“知道”這些變量的具體類(lèi)型,這稱為“反射機(jī)制”。在Go內(nèi)置的reflect包里定義了一個(gè)接口reflect.Type和一個(gè)結(jié)構(gòu)體reflect.Value,它們提供很多函數(shù)來(lái)獲取存儲(chǔ)在接口里的類(lèi)型信息。(1)reflect.Type接口:主要提供關(guān)于類(lèi)型相關(guān)的信息。(2)reflect.Value結(jié)構(gòu)體:主要提供關(guān)于值相關(guān)的信息,可以獲取甚至改變類(lèi)型的值。reflect包中提供了兩個(gè)基礎(chǔ)的函數(shù)來(lái)獲取上述的接口和結(jié)構(gòu)體,原型如下:funcTypeof(iinterface{})TypefuncValueOf(iinterface{})ValueGo語(yǔ)言中反射的使用遵循三大法則(又稱“反射三定律”,詳見(jiàn)《TheLawsofReflection》),如下:法則一:反射可以將“接口變量”轉(zhuǎn)換為“反射對(duì)象”。法則二:反射可以由“反射對(duì)象”創(chuàng)建出“接口變量”。法則三:如果要修改“反射對(duì)象”,則其值必須是“可寫(xiě)的”。01接口變量轉(zhuǎn)換為反射對(duì)象接口變量轉(zhuǎn)換為反射對(duì)象反射是一種檢查存儲(chǔ)在接口變量中的(類(lèi)型,值)對(duì)的機(jī)制。

溫馨提示

  • 1. 本站所有資源如無(wú)特殊說(shuō)明,都需要本地電腦安裝OFFICE2007和PDF閱讀器。圖紙軟件為CAD,CAXA,PROE,UG,SolidWorks等.壓縮文件請(qǐng)下載最新的WinRAR軟件解壓。
  • 2. 本站的文檔不包含任何第三方提供的附件圖紙等,如果需要附件,請(qǐng)聯(lián)系上傳者。文件的所有權(quán)益歸上傳用戶所有。
  • 3. 本站RAR壓縮包中若帶圖紙,網(wǎng)頁(yè)內(nèi)容里面會(huì)有圖紙預(yù)覽,若沒(méi)有圖紙預(yù)覽就沒(méi)有圖紙。
  • 4. 未經(jīng)權(quán)益所有人同意不得將文件中的內(nèi)容挪作商業(yè)或盈利用途。
  • 5. 人人文庫(kù)網(wǎng)僅提供信息存儲(chǔ)空間,僅對(duì)用戶上傳內(nèi)容的表現(xiàn)方式做保護(hù)處理,對(duì)用戶上傳分享的文檔內(nèi)容本身不做任何修改或編輯,并不能對(duì)任何下載內(nèi)容負(fù)責(zé)。
  • 6. 下載文件中如有侵權(quán)或不適當(dāng)內(nèi)容,請(qǐng)與我們聯(lián)系,我們立即糾正。
  • 7. 本站不保證下載資源的準(zhǔn)確性、安全性和完整性, 同時(shí)也不承擔(dān)用戶因使用這些下載資源對(duì)自己和他人造成任何形式的傷害或損失。

最新文檔

評(píng)論

0/150

提交評(píng)論