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)
類似引用類型的數據:
- 數組切片 : 指向數組的一個區間
- map : 鍵值對
- channel : 執行體間的通信設施
- 接口 : 對一組滿足某個契約的類型的抽象
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{})