<rt id="bn8ez"></rt>
<label id="bn8ez"></label>

  • <span id="bn8ez"></span>

    <label id="bn8ez"><meter id="bn8ez"></meter></label>

    少年阿賓

    那些青春的歲月

      BlogJava :: 首頁 :: 聯系 :: 聚合  :: 管理
      500 Posts :: 0 Stories :: 135 Comments :: 0 Trackbacks

    Go語言沒有沿襲傳統面向對象編程中的諸多概念,比如繼承、虛函數、構造函數和析構函數、隱藏的this指針等。

     

    方法

    Go 語言中同時有函數和方法。方法就是一個包含了接受者(receiver)的函數,receiver可以是內置類型或者結構體類型的一個值或者是一個指針。所有給定類型的方法屬于該類型的方法集。

    如下面的這個例子,定義了一個新類型Integer,它和int一樣,只是為它內置的int類型增加了個新方法Less()

    復制代碼
    type Integer int   func (a Integer) Less(b Integer) bool {     return a < b  }  func main() {     var a Integer = 1       if a.Less(2) {         fmt.Println("less then 2")     }    }
    復制代碼

    可以看出,Go語言在自定義類型的對象中沒有C++/Java那種隱藏的this指針,而是在定義成員方法時顯式聲明了其所屬的對象。

     

    method的語法如下:

    func (r ReceiverType) funcName(parameters) (results)

    當調用method時,會將receiver作為函數的第一個參數:

    funcName(r, parameters);

    所以,receiver是值類型還是指針類型要看method的作用。如果要修改對象的值,就需要傳遞對象的指針。

    指針作為Receiver會對實例對象的內容發生操作,而普通類型作為Receiver僅僅是以副本作為操作對象,并不對原實例對象發生操作。

    復制代碼
    func (a *Ingeger) Add(b Integer) {     *a += b }  func main() {     var a Integer = 1      a.Add(3)     fmt.Println("a =", a)     //  a = 4 }
    復制代碼

    如果Add方法不使用指針,則a返回的結果不變,這是因為Go語言函數的參數也是基于值傳遞。

    注意:當方法的接受者是指針時,即使用值類型調用那么方法內部也是對指針的操作。

     

    之前說過,Go語言沒有構造函數的概念,通常使用一個全局函數來完成。例如:

    復制代碼
    func NewRect(x, y, width, height float64) *Rect {     return &Rect{x, y, width, height} }     func main() {     rect1 := NewRect(1,2,10,20)     fmt.Println(rect1.width) }
    復制代碼

     

     


    匿名組合

    Go語言提供了繼承,但是采用了組合的語法,我們將其稱為匿名組合,例如:

    復制代碼
    type Base struct {     name string }  func (base *Base) Set(myname string) {     base.name = myname }  func (base *Base) Get() string {     return base.name }  type Derived struct {     Base     age int  }  func (derived *Derived) Get() (nm string, ag int) {     return derived.name, derived.age }   func main() {     b := &Derived{}      b.Set("sina")     fmt.Println(b.Get()) }
    復制代碼

    例子中,在Base類型定義了get()和set()兩個方法,而Derived類型繼承了Base類,并改寫了Get()方法,在Derived對象調用Set()方法,會加載基類對應的方法;而調用Get()方法時,加載派生類改寫的方法。

     

    組合的類型和被組合的類型包含同名成員時, 會不會有問題呢?可以參考下面的例子:

    復制代碼
    type Base struct {     name string     age int }  func (base *Base) Set(myname string, myage int) {     base.name = myname     base.age = myage }  type Derived struct {     Base     name string }  func main() {     b := &Derived{}      b.Set("sina", 30)     fmt.Println("b.name =",b.name, "\tb.Base.name =", b.Base.name)     fmt.Println("b.age =",b.age, "\tb.Base.age =", b.Base.age) }
    復制代碼

     

     

     


    值語義和引用語義

    值語義和引用語義的差別在于賦值,比如

    b = a b.Modify()

    如果b的修改不會影響a的值,那么此類型屬于值類型;如果會影響a的值,那么此類型是引用類型。

    Go語言中的大多數類型都基于值語義,包括:

    • 基本類型,如byte、int、bool、float32、string等;
    • 復合類型,如arry、struct、pointer等;

     

    C語言中的數組比較特別,通過函數傳遞一個數組的時候基于引用語義,但是在結構體定義數組變量的時候基于值語義。而在Go語言中,數組和基本類型沒有區別,是很純粹的值類型,例如:

    var a = [3] int{1,2,3} var b = a b[1]++ fmt.Println(a, b)   // [1 2 3] [1 3 3]

    從結果看,b=a賦值語句是數組內容的完整復制,要想表達引用,需要用指針:

    var a = [3] int{1,2,3} var b = &a    // 引用語義 b[1]++ fmt.Println(a, b)   // [1 3 3] [1 3 3]

     

     


    接口

    Interface 是一組抽象方法(未具體實現的方法/僅包含方法名參數返回值的方法)的集合,如果實現了 interface 中的所有方法,即該類/對象就實現了該接口。

    Interface 的聲明格式:

    type interfaceName interface {       //方法列表   }  

    Interface 可以被任意對象實現,一個類型/對象也可以實現多個 interface;
    interface的變量可以持有任意實現該interface類型的對象。

     如下面的例子:

    復制代碼
    package main      import "fmt"      type Human struct {         name string         age int         phone string     }      type Student struct {         Human //匿名字段         school string         loan float32     }      type Employee struct {         Human //匿名字段         company string         money float32     }      //Human實現SayHi方法     func (h Human) SayHi() {         fmt.Printf("Hi, I am %s you can call me on %s\n", h.name, h.phone)     }      //Human實現Sing方法     func (h Human) Sing(lyrics string) {         fmt.Println("La la la la...", lyrics)     }      //Employee重載Human的SayHi方法     func (e Employee) SayHi() {         fmt.Printf("Hi, I am %s, I work at %s. Call me on %s\n", e.name,             e.company, e.phone)         }      // Interface Men被Human,Student和Employee實現     // 因為這三個類型都實現了這兩個方法     type Men interface {         SayHi()         Sing(lyrics string)     }      func main() {         mike := Student{Human{"Mike", 25, "222-222-XXX"}, "MIT", 0.00}         paul := Student{Human{"Paul", 26, "111-222-XXX"}, "Harvard", 100}         sam := Employee{Human{"Sam", 36, "444-222-XXX"}, "Golang Inc.", 1000}         tom := Employee{Human{"Tom", 37, "222-444-XXX"}, "Things Ltd.", 5000}          //定義Men類型的變量i         var i Men          //i能存儲Student         i = mike             fmt.Println("This is Mike, a Student:")         i.SayHi()         i.Sing("November rain")          //i也能存儲Employee         i = tom         fmt.Println("This is tom, an Employee:")         i.SayHi()         i.Sing("Born to be wild")          //定義了slice Men         fmt.Println("Let's use a slice of Men and see what happens")         x := make([]Men, 3)         //這三個都是不同類型的元素,但是他們實現了interface同一個接口         x[0], x[1], x[2] = paul, sam, mike          for _, value := range x{             value.SayHi()         }     }
    復制代碼

     

    空接口

    空interface(interface{})不包含任何的method,正因為如此,所有的類型都實現了空interface??読nterface對于描述起不到任何的作用(因為它不包含任何的method),但是空interface在我們需要存儲任意類型的數值的時候相當有用,因為它可以存儲任意類型的數值。它有點類似于C語言的void*類型。

    復制代碼
    // 定義a為空接口     var a interface{}     var i int = 5     s := "Hello world"     // a可以存儲任意類型的數值     a = i     a = s
    復制代碼

     

    interface的變量里面可以存儲任意類型的數值(該類型實現了interface),那么我們怎么反向知道這個interface變量里面實際保存了的是哪個類型的對象呢?目前常用的有兩種方法:switch測試、Comma-ok斷言。

     

    switch測試如下:

    復制代碼
    type Element interface{} type List [] Element  type Person struct {     name string     age int  }  //打印 func (p Person) String() string {     return "(name: " + p.name + " - age: "+strconv.Itoa(p.age)+ " years)" }  func main() {     list := make(List, 3)     list[0] = 1 //an int      list[1] = "Hello" //a string     list[2] = Person{"Dennis", 70}       for index, element := range list{         switch value := element.(type) {             case int:                 fmt.Printf("list[%d] is an int and its value is %d\n", index, value)             case string:                 fmt.Printf("list[%d] is a string and its value is %s\n", index, value)             case Person:                 fmt.Printf("list[%d] is a Person and its value is %s\n", index, value)             default:                 fmt.Println("list[%d] is of a different type", index)         }        }    }
    復制代碼

     

    如果使用Comma-ok斷言的話:

    復制代碼
    func main() {     list := make(List, 3)     list[0] = 1 // an int     list[1] = "Hello" // a string     list[2] = Person{"Dennis", 70}      for index, element := range list {         if value, ok := element.(int); ok {             fmt.Printf("list[%d] is an int and its value is %d\n", index, value)         } else if value, ok := element.(string); ok {             fmt.Printf("list[%d] is a string and its value is %s\n", index, value)         } else if value, ok := element.(Person); ok {             fmt.Printf("list[%d] is a Person and its value is %s\n", index, value)         } else {             fmt.Printf("list[%d] is of a different type\n", index)         }     } }
    復制代碼

     

     

    嵌入接口

    正如struct類型可以包含一個匿名字段,interface也可以嵌套另外一個接口。

    如果一個interface1作為interface2的一個嵌入字段,那么interface2隱式的包含了interface1里面的method。

     

     

    反射

    所謂反射(reflect)就是能檢查程序在運行時的狀態。

    使用reflect一般分成三步,下面簡要的講解一下:要去反射是一個類型的值(這些值都實現了空interface),首先需要把它轉化成reflect對象(reflect.Type或者reflect.Value,根據不同的情況調用不同的函數)。這兩種獲取方式如下:

     t := reflect.TypeOf(i)    //得到類型的元數據,通過t我們能獲取類型定義里面的所有元素  v := reflect.ValueOf(i)   //得到實際的值,通過v我們獲取存儲在里面的值,還可以去改變值

     

    轉化為reflect對象之后我們就可以進行一些操作了,也就是將reflect對象轉化成相應的值,例如

    tag := t.Elem().Field(0).Tag  //獲取定義在struct里面的標簽 name := v.Elem().Field(0).String()  //獲取存儲在第一個字段里面的值

     

    獲取反射值能返回相應的類型和數值

    var x float64 = 3.4 v := reflect.ValueOf(x) fmt.Println("type:", v.Type()) fmt.Println("kind is float64:", v.Kind() == reflect.Float64) fmt.Println("value:", v.Float())

     

    最后,反射的話,那么反射的字段必須是可修改的,我們前面學習過傳值和傳引用,這個里面也是一樣的道理。反射的字段必須是可讀寫的意思是,如果下面這樣寫,那么會發生錯誤

    var x float64 = 3.4 v := reflect.ValueOf(x) v.SetFloat(7.1)

     

    如果要修改相應的值,必須這樣寫

    var x float64 = 3.4 p := reflect.ValueOf(&x) v := p.Elem() v.SetFloat(7.1)

    上面只是對反射的簡單介紹,更深入的理解還需要自己在編程中不斷的實踐。

     

     

    參考文檔:

    http://se77en.cc/2014/05/05/methods-interfaces-and-embedded-types-in-golang/

    http://se77en.cc/2014/05/04/choose-whether-to-use-a-value-or-pointer-receiver-on-methods/

     http://www.cnblogs.com/chenny7/p/4497969.html





    posted on 2017-08-03 11:34 abin 閱讀(444) 評論(0)  編輯  收藏 所屬分類: golang

    只有注冊用戶登錄后才能發表評論。


    網站導航:
     
    主站蜘蛛池模板: 亚洲伊人色一综合网| 人与动性xxxxx免费| 成人免费无码精品国产电影| 色噜噜狠狠色综合免费视频| 亚洲色大成网站WWW久久九九| 91av在线免费视频| 国产亚洲高清在线精品不卡 | 亚洲va中文字幕无码久久| 91短视频免费在线观看| 特黄aa级毛片免费视频播放| 午夜亚洲国产理论秋霞| 天天干在线免费视频| 免费无码又爽又刺激一高潮| 2020国产精品亚洲综合网| 亚洲中文字幕无码一区二区三区| 18勿入网站免费永久| 久久精品无码免费不卡| 国产亚洲精品VA片在线播放| 亚洲人成网亚洲欧洲无码久久| 噼里啪啦电影在线观看免费高清| 中国黄色免费网站| 亚洲日本在线电影| 亚洲国产一区二区a毛片| 免费一级一片一毛片| 国产又黄又爽又猛免费app| 久久www免费人成精品香蕉| 亚洲欧美日韩综合俺去了| 久久夜色精品国产噜噜噜亚洲AV| 波多野结衣一区二区免费视频| 日本免费xxxx| 全免费a级毛片免费看| 国产亚洲精品第一综合| 亚洲大成色www永久网址| 久久久久亚洲Av片无码v | 亚洲∧v久久久无码精品| 又色又污又黄无遮挡的免费视| 久久精品国产免费观看 | 最近免费中文字幕大全免费版视频 | 亚洲黄色网址大全| 亚洲精品亚洲人成在线观看| 又色又污又黄无遮挡的免费视|