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 := {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{})