5 面向對象

1 類型系統

1.1給類型添加方法

// 爲類型添加方法
type Integer int  // 定義一個新類型Integer, 它與int類型相同

func (a Integer) Less(b Integer) bool {    // 給Integer類型添加新方法 less, less 方法輸入一個整數,返回布爾值
    return a < b  // 當前整數a調用less方法要傳入一個整數b, 如果a<b,返回true
}

如果我們想在方法中修改對象,這種時候必須就要用到指針

func (a *Integer) Add (b Integer) {
    *a += b
}

func main()  {
    a := Integer(10)
    a.Add(5)
    fmt.Println("a is : ", a)
}

 1.2 值語義和引用語義

b = a
b.Modyfy()
// 如果a的值收到影響則a爲引用類型,否則爲值類型

值類型的數據:

  • 基本數據類型
  • 符合類型: 數組, 結構體, 指針
// 數組想表達引用
var a = [3]int{1,2,3}
var b = &a
b[1]++
fmt.Println(a, *b)

類似引用類型的數據:

  1. 數組切片 : 指向數組的一個區間
  2. map : 鍵值對
  3. channel : 執行體間的通信設施
  4. 接口 : 對一組滿足某個契約的類型的抽象

1.3 結構體

定義矩陣類型

type Rect struct {。      // 定義矩形類型
    x, y float64
    width, height float64
}

func (r *Rect) Area() float64{    //定義成員方法計算矩形面積
    return r.width * r.height
}

2. 初始化

初始化Rect類型的實例

rect1 := new(Rect)
rect2 := &Rect{}
rect3 := &Rect{0, 0, 100, 200}
rect4 := {width:100, height:200}

3 匿名組合

type Base struct {   // 定義Base類
    Name string
}

func (base *Base) Par() {   // Base類實現Par方法和Run方法
    fmt.Println("par in Base")
}

func (base *Base) Run() {    
    fmt.Println("run in base")
}

type Foo struct {      // Foo類繼承Base類
    Base
}

func (foo *Foo) Run() {    // Foo類重寫Run方法,先執行Base的Run方法在執行自己的代碼
    foo.Base.Run()
    fmt.Println("run in foo")
}

func main()  {
    foo := new(Foo)  // 實例化一個foo對象,該對象有繼承自父類的Par方法和自己的Run方法
    foo.Par()
    foo.Run()
}

 4.接口

4.1 非侵入式接口

go語言中,一個類只需要實現了接口要求的所有函數,我們就說這個類實現了該接口

例如:

type File  struct{

}

func (f *File) Read (buf []byte) (n int, err error){
    return
}

func (f *File) Write(buf []byte) (n int, err error) {
    return
}

func (f *File) Seek (buf []byte) (n int, err error) {
    return
}

func (f *File) Close () (e error) {
    return
}

type IFile interface {
    Read(buf []byte)(n int, err error)
}

type IReader interface {
    Read(buf []byte)(n int, err error)
    Write(buf []byte)(n int, err error)
    Close(buf []byte)(n int, err error)
}

func main()  {
    var file1 IFile = new(File)
    fmt.Println(file1)
}

我們定義了一個File類,並實現了Read, Write, Seek, Close等方法。我們有IFile,IReader接口,儘管File類沒有從這些接口繼承,甚至不知道這些接口的存在,但是File類實現了這些接口,可以進行賦值

4.2 接口賦值

接口賦值的兩種情況:

  1. 將對象賦值給接口

  2. 將一個接口賦值給另一個接口

type Integer1 int

func (a Integer1)Less(b Integer1) bool {
    return a < b
}

func (a *Integer1)Add(b Integer1)  {
    *a += b
}

type Lessadd interface {
    Less(b Integer1) bool
    Add(b Integer1)
}

type Less interface {
    Less(b Integer1) bool
}

func main()  {
    int1 := Integer1(5)
  // 將對象賦值給接口
var ld Lessadd = &int1 //注意這兩種情形的區別,因爲Integer1類Add方法中我們要改變自身需要傳遞指針,所以在給接口賦值時也要傳遞指針。否則編譯不通過 var ll Less = int1 // 這裏不需要傳遞指針 fmt.Println(int1, ld.Less(6), ll) }

 在go語言中,只要兩個接口擁有相同的方法列表,次序不同不要緊,那麼他們就是等同的,可以相互賦值

package one

type ReaderWriter interface {
    Read(buf []byte) (n int, err error)
    Write(buf []byte) (n int, err error)
}
package two

type IStream interface{
    Write(buf []byte)(n int, err error)
    Read(buf []byte)(n int, err error)
}

兩個包中分別有兩個接口,這兩個接口有同樣的方法列表,只是次序不同,他們可以相互賦值

func main()  {
    var f1 two.IStream = new(File)
    var f2 one.ReaderWriter = f1
    var f3 two.IStream = f2
    fmt.Println(f1, f2, f3)
}

接口賦值並不要求兩個接口完全的等價,只要A的方法列表B包含在的方法列表中,那麼B的對象即可賦值給A接口

package main

import "fmt"

type Animal interface{
    Speaking() string
}

type Cat struct{}

func (c *Cat) Speaking()string  {
    return "喵喵喵"
}

type Sheep struct {}

func (s Sheep)Speaking()string  {
    return "咩咩咩"
}

type Zhangbin struct {}

func (z Zhangbin)Speaking()string  {
    return "汪汪汪"
}



func main()  {
        var animals = []Animal{&Cat{}, Sheep{}, Zhangbin{}}    // 注意這裏必須使用指針來實例化cat對象,因爲cat的指針有speaking方法,而cat對象本身不具備speaking方法
        fmt.Println(animals)
    }

 

4.3 接口查詢

type Writer interface {
    Write(buf []byte)(n int, err error)
}

func main()  {
    var f1 two.IStream = new(File)
    var f2 one.ReaderWriter = f1
    var f3 two.IStream = f2

    var f4 Writer = f1   // IStream實現了Write方法,所以可以賦值給Writer
    //var f5 two.IStream = f4  // 因爲Writer沒有實現Read方法,所以不能賦值給IStream


    fmt.Println(f1, f2, f3, f4, f5)
}

 

var file1 Writer = ...
if file5, ok := file1.(two.IStream);ok {
    ...  
}


// if語句查詢file1接口指向的對象實例是否實現了two.IStream接口
type File struct {}    // 創建File類, 這個類實現了 Write, Read, Close方法

func (f *File)Write()  {
    return
}

func (f *File)Read()  {
    return
}

func (f *File)Close()  {
    return
}

type WR interface{           // WR接口
    Write()
    Read()
}

type W interface {            // W接口
    Write()
}

func main()  {
        var wr1 W = new(File)
        _, ok := wr1.(WR)            // 查看wr1指向的對象是否實現了了WR接口
        fmt.Println(wr1, ok)
    }

查詢接口所指向的對象實例是否爲某個類型

var file1 Writer = ...
if file6, ok := file1.(*File); ok {
    ...
}

4.4 類型查詢

type File struct {}

func (f *File)Write()  {
    return
}

func (f *File)Read()  {
    return
}

func (f *File)Close()  {
    return
}

type WR interface{
    Write()
    Read()
}

type W interface {
    Write()
}

func main()  {
        var wr1 WR = new(File)
        _, ok := wr1.(WR)
        fmt.Println(wr1, ok)
        switch wr1.(type){          // 類型查詢語句
        case W:
            fmt.Println("type is W")
        case WR:
            fmt.Println("is WR")
        }
    }

4.5 接口組合 

type ReadWriter interace {        // ReadWriter接口將Reader接口和Writer接口組合起來
    Reader
    Writer
} 

4.6 Any類型

go語言中任何對象實例都滿足空接口interface{}, 所以interface{} 看起來像是可以指向任何對象的Any類型

var v1 interface{} = 1
var v2 interface{} = "abc"
var v3 interface{} = &v2
var v4 interface{} = struct{ X int }{1}
var v5 interface{} = &struct{ X int }{1}

當函數可以接受任意的對象實例時, 我們會將其聲明爲interface{}

func Printf(fmt string, args ...interface{})
func Println(args ...interface{})

 

 

 

 

 

 

 

 

 

                                                                                                                                                                                                                                   

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章