Go语言自学笔记(四)

版权声明:本文为博主原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。
本文链接: https://blog.csdn.net/qq_18800771/article/details/97250277

面向对象编程:继承,封装,多态,重用,多线程,低耦合。

封装:通过方法实现。

继承:通过匿名字段实现。

多态:通过接口实现。

Go语言的匿名字段/匿名组合/嵌入字段:

结构体类型匿名字段:匿名字段既会继承字段的成员变量,又会继承字段的方法(方法在下文详解)。

type Person struct{
    name string
    sex byte
    age int
}
type Student struct{
    Person    //匿名字段,只有类型,没有名字。继承字段实现代码复用即继承了Person全部成员
    id int
    addr string
}
func (tmp *Person) PrintInfo(){    //Person实现的方法,通过匿名字段可以被Student调用
    fmt.Printf("name=%s, sex=%c, age=%d\n",tmp.name,tmp.sex,tmp.age)
}

匿名字段的初始化:

顺序初始化/全部初始化:

var s1 Student = Student{Person{"tom",'m',18},1,"北京"}
fmt.Printf("s = %+v",s)    //格式化打印:%+v占位,详细输出

指定成员初始化,没有初始化的成员自动赋值为0:

s2:=Student{Person:Person{name:"tom"},addr:"北京"}

成员的操作:与结构体完全一致

匿名字段的整体赋值:

s1.Person=Person{"tom",'m',18}

处理匿名字段中的同名字段:

type Person struct{
    name string
    sex byte
    age int
}
type Student struct{
    Person
    id int
    addr string
    name string    //同名字段
}

赋值同名字段:就近原则,与同名局部/全局变量规则相同。如果能在本作用域找到此成员则对起进行操作,如果没有找到则找继承的字段操作。直接操作继承字段的同名字段,则运用以下语法:显式调用

s1.Person.name = "go"

非结构体匿名字段:

type Person struct{
    name string
    sex byte
    age int
}
type Student struct{
    Person    //结构体匿名字段
    int        //非结构体匿名字段
    string
}

赋值时与正常结构体无区别,但要注意的是在使用非结构体匿名字段时,可以直接用其类型名来代替成员名使用。如:

s1.Person
s1.int

结构体指针类型匿名字段:

type Student struct{
    *Person    //指针匿名字段
    id int
    addr string
}

指针匿名字段初始化:在对指针类型进行整体打印时只会显示内存地址。

直接初始化:

s1 := Student{&Person{"tom",'m',18},1,"北京"}

分配空间及赋值初始化:

var s2 Student
s2.Person=new(Person)    //分配空间
s2.name="tom"
s2.sex='m'
s2.age=18
s2.id=1
s2.addr="北京"

方法/method绑法函数:func (reviver Type) funname(){}

接收者reciver,绑定类型Type。带有接收者的函数就叫做方法。

通过方法讨论面向过程和变相对象函数的区别:

面向过程:

func add(a,b int)int{
    return a+b
}
func main(){
    result:=add(1,1)
}

面向对象:

type long int    //int别名为long
func (tmp long) add(other long) long{    //add方法绑定类型接收者,tmp接收者,就是一个传递参数
    return tmp+other
}
func main(){
    var a long = 2
    result:=a.add(3)    //调用格式:变量名.函数(参数)
}

需要注意的是:Go语言类型检查非常严格,此时long与int不能相互接收。面向对象函数只是换了一种表现形式。

结构体类型绑定添加函数方法:

type Person struct{
    name string
    sex byte
    age int
}
func (tmp Person) PrintInfo(){
    fmt.Println(tmp)
}
func main(){
    p:=Person{"tom",'m',18}
    p.PrintInfo()
}

指针作为接收者:

func (p *Person) SetInfo(n string,s byte,a int){
    p.name=n
    p.sex=s
    p.age=a
}
func main(){
    var p2 Person
    (&p2).SetInfo("tom",'m',18)
    p2.PrintInfo()
}

需要注意的是:接受者不能原本就是指针类型,方法不支持重载/重复定义(接收者类型不同,方法同名不算做重载)。

值语义和引用语义:传递类型

接收者为普通变量:值语义(复制)

func (p Person) SetInfoValue(n string,s byte,a int){
    p.name=n
    p.sex=s
    p.age=a
}
func main(){
    s1:=new(Person)
    s1.SetInfoValue("tom",'m',18)
}

接收者为指针变量:引用语义(地址传递)

func (p *Person) SetInfoValue(n string,s byte,a int){
    p.name=n
    p.sex=s
    p.age=a
}
func main(){
    s1:=new(Person)
    (&s1).SetInfoValue("tom",'m',18)
}

Go语言中的方法集:定义的变量可以调用的方法就是方法集。

指针变量调用普通变量:

正常调用:(*p).SetInfoValue()

内部转换:p.SetInfoValue() 先把指针p转为*p再调用

指针变量调用指针变量:

正常调用:p.SetInfoPointer()

内部转换:(*p).SetInfoValue()先把*p转换成p再调用

指针变量调用指针变量:

内部转换:p.SetInfoPointer() 内部先把p转化为&p再调用

普通变量调用普通变量:

正常调用:p.SetInfoValue()

总结:用实例value和pointer调用方法(含匿名字段)不受方法集的约束,编译器总是查找全部方法,并自动转换receiver实参。

方法的继承:方法可以通过匿名字段继承给其他结构体,其他结构体可以直接调用。

方法的重写/同名方法:

type Person struct{
    name string
    sex byte
    age int
}
type Student struct{
    Person
    id int
    addr string
}
func (tmp *Person) PrintInfo(){    //Person实现的方法,通过匿名字段可以被Student调用
    fmt.Printf("name=%s, sex=%c, age=%d\n",tmp.name,tmp.sex,tmp.age)
}
func (tmp *Student) PrintInfo(){    //Srudent实现的同名方法
    fmt.Printfln(tmp)
}

同名方法的调用:就近原则,优先本作用域方法。若直接操作继承的同名方法,则需要显式方法。

s.Person.PrintInfo()

方法值和方法表达式:

func (p Person) SetInfoValue(){
    fmt.Printf("SetInfoValue: %p, %v\n", &p, p)
}
func (p *Person) SetInfoPointer(){
    fmt.Printf("SetInfoPointer: %p, %v\n", p, p)
}

方法值:函数指针的使用。

p.SetInfoPointer()    //传统调用方式
pFunc:=p.SetInfoPointer    //方法值保存函数入口
pFunc()    //调用函数,不需要再传递接收者,接收者被隐藏

方法表达式:

f1:=(*Person).SetInfoPointer    //显式传递接收者
f1(&p)        //也可去掉&
f2:=(Person).SetInfoValue
f2(p)

Go语言的接口:描述了一系列方法的结合,只声明函数不去实例化。只关心实现的行为。

接口的定义:

type Humaner interface{
    sayhi()        //只有声明,由自定义类型实现
}

接口的实现:

func (tmp *Student) sayhi(){
   }

接口变量的定义和赋值,调用:

var i Humaner
s:=&Student{}
i=s
i.sayhi()    //只要实现了此接口方法的类型,那么这个类型的变量就可以给i赋值,调用自动匹配方法

多态的实现:定义普通函数,函数的参数为接口类型:

func WhoSayHi(i Humaner){
    i.sayhi()    //只有一个函数,拥有不同表现--多态
}

接口调用:

WhoSayHi(s)    //普通调用
x:=make([]Humaner, 1)    //创建切片
x[0]=s
for _,data:=range x{    //通过迭代调用
    i.sayhi()
}

接口的继承:

type Humaner interface{    //子集
    sayhi()
}
type Personer interface{    //超集
    Humaner    //匿名字段,继承了sayhi()
    sing(lrc string)    //含有参数的方法
}
func (tem *Student) sayhi(){
}
func (tem *Student) saing(lrc string){
}
func main(){
    var i Personer
    s:=&Student{}
    i=s
    i.sayhi()    //继承的方法
    i.sing("song")
}

接口转换:超集变量可以转换为子集,但反过来不行

var iPro Personer    //超集
var i Humaner    //子集
iPro=i    //err
i=iPro

空接口类型:万能类型,可以保存任何类型的值。没有方法,所有类型都实现了这个接口。

var i interfave{}=1
i="abc"
var a []int        //切片空接口
var b []interface{}        //空接口

参数为空接口类型的函数:

func fun(args ...interface{}){
}    //可以报错0-多个不限类型的参数

类型断言/类型查询:

通过if的类型断言:

func main(){
    i := make([]interface{}, 3)
	i[0] = 1    //int
    i[1] = "hello go"    //string
    i[3] = Student{}    //struct
    for index, data := range i{        //index返回的是下标,data返回空接口变量
        if value, ok := data.(int); ok==true{        //value返回接口变量本身,ok返回bool类
            fmt.Printf("x[%d] 类型为int,内容为%d\n", index, value)
        }else if value, ok := data.(string); ok==true{
            fmt.Printf("x[%d] 类型为string,内容为%s\n", index, value)
        }else if value, ok := data.(Student); ok==true{
            fmt.Printf("x[%d] 类型为Student,内容为name=%s,id=%d\n", index, value.name, value.id)
        }
}

通过switch的类型断言:

for index, data := range i{
    switch value := data.(type){
    case int:
        fmt.Printf("x[%d] 类型为int,内容为%d\n", index, value)
    case string:
        fmt.Printf("x[%d] 类型为string,内容为%s\n", index, value)
    case Student:
        fmt.Printf("x[%d] 类型为Student,内容为name=%s,id=%d\n", index, value.name, value.id)
    }
}

通过格式化输出的类型断言:

for index, data := range i{
    fmt.Printf("x[%d]的类型为%T,内容为%v\n", index, value, value)
}    //自动类型的类型断言

猜你喜欢

转载自blog.csdn.net/qq_18800771/article/details/97250277