第四章 Go语言面向对象编程
一:为类型添加方法 Go这你可以为任何类型添加方法(不包括指针类型) 例如
type Integer intfunc (a Integer) less(b Integer) bool {return a < b}func main() {var a Integer =3if a.less(2) {fmt.Println("返回的值a大于b")}else {fmt.Println("返回值 a小于b")}}只有你必须改变对象的时候才要用指针类型 如下://a 是指针类型传递,这个可以改变a的值func (a *Integer) Add(b Integer) {*a+=b}//a是非指针类型传递 这个不可以改变a的值func (a Integer) Addd(b Integer) {a+=b}调用方式var t Integer =3t.Add(6)fmt.Println("面向对象给类添加方法:",t)t.Addd(8)fmt.Println("面向对象给类添加方法这个传递的不是指针:",t)输出的值面向对象给类添加方法: 9面向对象给类添加方法这个传递的不是指针:9在Go中数组被定义为彻底的值类型,所以要改变数组值就要做指针传递 ,例如//这个是一般的复制var h =[3]int{1,2,3}var b = hb[1]++fmt.Println("输出h的值:",h,"输出b的值:",b)//这个是指针传递var c =&hc[1]++fmt.Println("指针传递输出h的值:",h,"输出b的值:",*c)var c =&h这句表示复制的事 h的引用,所以c不是[3]int类型,而是指针*[3]int类型
二:四个特殊类型分析 1):数组切片,数组切片本质上一个区间,因为数组切片内部是指向数组的指针,所以可以改变数组切片的元素(这也是为什么数组不能动态添加元素,但是切片可以的原因),但是数组切片类型本身的赋值仍然是值类型
2)map本质上是一个字典指针,
3)channel和map类似也是一个指针,channel和map不设计成值类型是因为,完整赋值map和chanel不是常规需求
4 )接口,接口是一个引用类型,内部维护了两个指针
以前我们说过,所有的Go语言类型(除了指针类型)都可以有自己的方法,在这个背景下,GO语言的结构体只是一个很普通符合类型, 比如:
type myup struct {x, y intw,h int}func (myu *myup) area() int {return myu.x*myu.y}
三:初始化 在定义了myup类型后,该如何创建并初始化myup类型,如下
type myup struct {
x, y int
w,h int
}这样来创建使用myq :=new(myup)myq2 :=&myup{}myq3 :=&myup{1,2,3,4}myq4 :=&myup{x:100,w:22}在Go语言中为显示初始化的变量,都会被初始化为该类型的零值,例如 bool的零值是 false,int的零值是0 string的零值是空字符串在Go 语言中没有构造函数的概念,对象的创建通常交给一个全局的创建函数完成,以Newxxx来命名,表示构造函数 如:func Newmyup(x,y,w,h int) *myup {return &myup{x,y,w,h,}}
四:匿名组合 准确的说Go语言也提供了继承,但是采用的是组合的方法,所以我们称为匿名组合
type Base struct {Name string}func (base *Base) Foo() {}func (base *Base) Bar() {}type Foo struct {Base}func (foo *Foo) Bar() {foo.Base.Bar()}
例如上面,定义了一个 Base基础类型,然后实现了Foo,Bar两个方法,然后定义了一个 Foo类,该类从Base类继承,并改写了Bar方法(该方法实现时先调用了积累的Bar方法),这样Go语言就清晰的告诉我们类的内存布局是怎么的啦,另外 在Go语言中还可以以指针的方式从一个类型继承(派生)type MyFoo struct {*Base} 这样在创建MyFoo实例的时候需要外部提供一个Base类型的指针func mmy() {myfoo :=&Base{}myfoo.Base.Bar()}另外需要注意的就是 组合类型命名冲突的问题 如:type myj struct {Name string}type Myk struct {myjName string}func (myo *Myk) Add {fmt.Println(myo.Name)fmt.Println(myo.myj.Name)}
其实这样是不会冲突的,所有的Myk类型Name成员访问的事最外成的那个Name变量,myj.Name相当于被隐藏了起来,
但是这样的就会报错type Logger struct {Level int}type Y struct {*LoggerName string*log.Logger}因为匿名组合类型相当于以其类型名称(去掉包名)作为成员变量的名称,所以Y里面相当于有两个 Logger成员还需要注意一点,Go语言中成员的可访问性是包一级别的,不是类型一级别的,就是在同一个包内,不同的类型也可以访问其他类型的成员
五:接口 接口在Go语言中有着至关重要的作用,可以说是Go语言的基石
在go中实现类的时候,只需要关心自己应该提供哪些方法,不用纠结接口的分拆
package mainimport "fmt"//接口测试type File struct {}func (f *File) Read(buf int) (n int,err error) {fmt.Println("添加读文件")return 2,nil}func (f *File) Wrire(buf int) (n int,err error) {fmt.Println("添加些文件")return 3,nil}func (f *File) Seek(off int64,whence int) (pos int64,err error) {fmt.Println("跳跃读取")return 4,nil}func (f *File) Close() error {fmt.Println("关闭文件")return nil}type IFile interface {Read(buf int) (n int, err error)Wrire(buf int) (n int,err error)Seek(off int64,whence int) (pos int64,err error)Close() error}type IRead interface {Read(buf int) (n int, err error)}type IWrire interface {Wrire(buf int) (n int,err error)}type IClose interface {Close() error}func main() {//File 虽然没有继承上面那些接口,可以说 不知道那些接口的纯在,但是File类已经实现了这些接口,如下面 可以进行赋值buyt := 3var file1 IFile=new(File)file1.Read(buyt)var file2 IRead =new(File)file2.Read(buyt)}输出:添加读文件
添加读文件
六:接口赋值
Go中接口赋值分为两种情况 1):将对象实例赋值给接口 2):将一个接口赋值给另一个接口
1:将对象实例赋值给接口,要求给对象实现了接口要求的所有方法
type Integer intfunc (a Integer) Less(b Integer) bool {return a<b}func (a *Integer) Add(b Integer) {*a+=b}type LessAdd interface {Less(b Integer) boolAdd(b Integer)}var a Integer=1var b LessAdd=&ab.Add(3)fmt.Println("对象实例赋值给接口 %d",b)对象实例赋值给接口 %d 0xc0420620a0
七:接口赋值给接口
type ReadWriter interface {Read(buf int) (n int, err error)Write(buf int) (n int,err error)}type IStream interface {Write(buf int) (n int,err error)Read(buf int) (n int, err error)}func rwgo() {//测试接口赋值接口var file1 ReadWriter =new(File)var file2 IStream=file1var file3 ReadWriter=file2fmt.Println("数据",file3)}接口赋值的两个接口必须是等价的, 如果a接口的方法列表是b接口方法列表的子集,那么b接口可以赋值给a接口 ,但是a接口不能赋值给b接口
八:接口查询 代码如下
type IStream interface {Write(buf int) (n int,err error)Read(buf int) (n int, err error)}type IWrite interface {Write(buf int) (n int,err error)}func chaxunjiekou() {//如上面的接口 IWrite,怎么转化为 IStream接口,(IWrite接口少了一个Read方法,)这就要用到我们的接口查询啦,接口查询是在运行期才能确定的var file1 IWrire= ...if file5,ok :=file1.(IStream);ok {}
九:接口组合
这种形式就是接口组合type My1 interface {Read(buf int) string}type My2 interface {Write(buf string) int}type My interface {My1My2}
这种组合的方式相当于把 My1和My2里面的声明都放到了My里面和这种是一样的
type MyTest interface {Read(buf int) stringWrite(buf string) int}这个和前面我们说的的 ,类的匿名组合有点相识
10:Any类型
在Go中任何对象实例都满足空接口interface{},所以interface{}可以看做是任何对象的Any类型 如下
var v1 interface{}=1var v2 interface{}="aa"var v3 interface{}=&v2var v4 interface{}= struct {x int}{1}当对象可以接受任何类型的时候,我们会将其声明为interface{},比如我们的printxx库就是一个例子func printf(fmt string, args ...interface{})