Go基础编程---面向对象

面向对象

Go中没有发封装、继承、多态,但是可以通过方法、匿名字段、接口实现,Go中偏向于面向接口编程

匿名字段(继承)

一般定义结构体时字段名与其类型一一对应,但是Go也支持 只提供类型,而不写字段名的方式,也就是匿名字段,也称嵌入字段,当匿名字段也是一个结构体的时候,那么这个结构体所拥有的全部字段都被隐式引入当前定义的这个结构体


type Animal struct {
    Name  string
    Age   int
    Color string
}
type Cat struct {
    Animal
    Types string
}

func main() {
    var cat Cat
    fmt.Println(cat)         // {{ 0 } }
    fmt.Printf("%+v\n", cat) // {Animal:{Name: Age:0 Color:} Types:}
    // 结构体初始化的几种方式
    // 部分成员初始化
    cat1 := Cat{Animal: Animal{Name: "zz", Color: "red"}, Types: "龙猫"}
    fmt.Printf("%+v\n", cat1) // {Animal:{Name:zz Age:0 Color:red} Types:龙猫}
    // 全部成员初始化
    cat2 := Cat{Animal{"gg", 2, "red"}, "老虎猫"}
    fmt.Printf("cat2 = %+v\n", cat2) // cat2 = {Animal:{Name:gg Age:2 Color:red} Types:老虎猫}

    // 结构体成员操作---取值
    /*cat2.Name        // -> gg
    cat2.Animal.Name // -> gg
    cat2.Types */ // -> 老虎猫, 若Animal中也有Types,则取值就近原则
    // 结构体成员操作---赋值
    cat2.Name = "CC"                 // -> "CC"
    cat2.Animal.Name = "DD"          // -> "DD"
    fmt.Printf("cat2 = %+v\n", cat2) // cat2 = {Animal:{Name:gg Age:2 Color:red} Types:老虎猫}

    // 其他匿名字段(非结构体类型,但是应用场景很少)
    // 所有的内置类型和自定义类型都是可以作为匿名字段的
    type myStr string // 自定义类型
    type Dog struct {
        Animal //匿名字段,结构体类型
        myStr  //匿名字段,自定义类型(少用)
        int    //匿名字段,内置类型(少用)
    }
    fmt.Println("----------------------------------------------------")
    // 结构体指针类型
    type Person struct {
        Name string
        Age  int
        Sex  byte
    }
    type Student struct {
        *Person // 匿名字段,结构体指针类型
        Id      int
        Addr    string
    }
    // 结构体指针类型的初始化
    people := Student{&Person{"zhangsan", 12, 1}, 1001, "beijing"}
    fmt.Printf("%+v \n", people) //people = {Person:0xc42000a060 Id:1001 Addr:beijing}
    // 结构体指针类型的操作
    var people1 = new(Person) // 分配空间
    people.Name = "xx"
    fmt.Printf("%+v ,%s \n", people, people.Name) //{Person:0xc42000a060 Id:1001 Addr:beijing} ,xx 
}

方法(封装)

在go中一个对象其实也就是一个简单的值或者是一个变量,在这个对象中会包含一些函数,这种带有接收者的函数,我们称为方法,本质上一个方法则是一个和特殊类型关联的函数
在Go中我们可以给任意自定义类型(包括内置类型、但是不包括指针类型)添加相应的方法,方法总是绑定对象实列

方法语法以及特点

func (receiver ReceiverType) funcName( parameters) (results)
* 参数reciver 可任意命名,如果方法中未使用,可省略参数名
* 参数reciver类型可以式T,或者*T,基本类型T不能是指针或者接口
* 不支持方法重载,也就是说不能定义名字相同但是不同参数的方法,但是只要接收者不一样,那么这2个函数就是不一样的函数
type Ins interface {
    Run()
}
type Inss *int

func (i Ins) Run() {
    //invalid receiver type Ins (Ins is an interface type)
}
func (i Inss) Run() {
    //invalid receiver type Inss (Inss is a pointer type)
}

自定义类型添加方法

type long int // 自定义一个类型,实际就是int型
func (l long) Add(n long) long {
    return l + n
}
func main() {
    //  l, n := 1, 2
    //  result := Add(l, n)
    //  fmt.Println(result) // undefined: Add
    var lo long = 2
    //  var i int
    fmt.Println(lo.Add(1))
    i := lo.Add(6) //cannot use lo.Add(1) (type long) as type int in assignment
    // 强类型,虽然返回的long类型实际是int,但是int类型的变量不能接收long类型的,
    var c = 6  // 自动推断c为int类型
    d := lo.Add(c) //报错不能传递int类型
    //  函数的参数虽然为long型,但是传递的是常量,Go能自动的将int 转为long,如果参数是变量,则有了类型,只要不是long类型就会报错
    fmt.Println(i) // 8
    fmt.Println(d) // cannot use c (type int) as type long in argument to lo.Add
}

结构体类型添加方法

type Person struct {
    Name string
    Sex  byte
    Age  int
}

// 定义一个结构体的方法,结构体类似于class,里面的变量为class的属性,结构体方法
func (p Person) Read() {
    fmt.Println(p.Name, "读书")
}
func (p *Person) SetInfo(name string, sex byte, age int) {
    p.Name = name
    p.Sex = sex
    p.Age = age
}
func main() {
    // 实例化结构体
    var p = Person{"zhangsan", 1, 12}
    p.Read()
    // 实例化结构体
    var p2 = Person{"李四", 1, 12}
    p2.Read()
    var p3 Person // 定义一个Person类型的变量
    p3.SetInfo("王五", 2, 11)
    fmt.Println(p3) //{王五 2 11}
}

值语义和引用语义

值语义: func ( p People) funName (xx){}    // 拷贝的副本
引用语义:func( p *People) funName(xx){}  //传递地址值

方法集

无论对象是指针类型还是值类型,都能调用,因为Go内部做了转换
eg:
type  Po struct{ Name string}
func (p Po) run() {}
func (p *Po) eat() {}
//指针对象
p := &Po{ "gao"}  //p可以调run 和 eat,在调run时内部p转换成了( *p )
//实例对象
p :=Po { "gao"}  //p可以调run 和 eat,在调eat时内部p转换成了( &p )

方法的继承和重写---方法表达式和方法值

结构体组合,除了能继承字段也能继承方法
type Person struct {
    Name string
    Sex  byte
    Age  int
}
type Student struct {
    Person // 不仅仅继承了Person的属性还继承他的Eat方法
    Id     int
}

// 给Person类型定义了一个方法
func (p Person) Eat() {
    fmt.Println(p.Name, "eating")
}

// 给Student 类型也定义一个和Eat方法,导致重写了Peson的Eat方法
func (s Student) Eat() {
    fmt.Println(s.Name, "student is eating")
}
func main() {
    p := Student{Person: Person{Name: "zzz"}}
    p.Eat()        //zzz student is eating
    p.Person.Eat() // zzz eating
    fmt.Println("--------------方法值和方法表达式---------------------")
    pFunc := p.Eat   // 这个就是方法值,调用函数时无需再传递接收者,隐藏了接收者,调用直接 pFunc()即可
    pFuncss :=(Stundent).Eat  // 方法表达式,类型为指针就得加指针 pFuncss(p)
    pFuncss :=(*Student).Eat   // 调用时 pFuncss(&p)
        
}

接口(多态实现)

Go中interface是一个自定义的抽象类型,不能将其实例化,内部只是描述一系列方法的集合

定义: 
type 接口名 interface{
      SayHi(a,b string) string
      ....
}
接口名习惯以er结尾
接口只有方法声明,没有实现,没有数据字段
接口可以匿名嵌入其他接口,或者嵌入到结构中去
接口是用来定义行为的类型,这个被定义的行为通过方法由用户定义的类型实现。实现了这个方法的具体类型就是这个接口类型的实例

接口的实现


type People struct {
    Name string
    Age  int
}
type Teacher struct {
    People
}
type Student struct {
    People
}
type str string
type Worker interface {
    Work()
}

func (strs str) Work() {
    fmt.Println("str")
}
func (t Teacher) Work() {
    fmt.Println(t.Name, "讲课")
}
func (s Student) Work() {
    fmt.Println(s.Name, "学习")
}
// 此函数的参数必须是实现了Worker接口的对象
func work(i Worker) {
    i.Work()
}
func main() {
    // 定义一个worker接口类型不能实例化,只能使得其他实现worker接口的实例赋值给此变量
    var inter Worker
    t := &Teacher{People{"laoshi", 55}}
    s := &Student{People{"xuesheng", 22}}
    var strs str
    inter = t
    inter.Work()
    inter = s
    inter.Work()
    inter = &strs
    inter.Work()
    //var aas int
    //inter = aas //cannot use aas (type int) as type Worker in assignment
    //inter.Work()
    // 调用同一函数,不同表现,多态,多种形态
    work(t)
    work(s)
    work(&strs)
    // 创建一个切片,每个内容都是一个实现了Worker接口的实例
    x := make([]Worker, 3)
    x[0] = t
    x[1] = s
    x[2] = &strs
    for _, v := range x {
        v.Work()
    }
}

接口的赋值(2种情况)

1、将对象实例赋值给接口(要求对象实现了接口的所有方法)
     - 类实例化时指针或者实例区别:
         o: 用new(structName):这个方法得到的是*structName类型,即类的指针类型
         o: 用structName{init para}:这个方法得到的是structName类型,即类的实例类型,不是指针
      -  类在实现接口的时候,要注意定义的时候,一般用类的指针传入,就可以了。   
          因为如果用类的结构体的话,可能会导致“类没有实现接口中某个方法”的错误
          

      
2、将一个接口赋值给另一个接口(假设接口A中定义的所有方法,都在接口B中有定义,   
那么B接口的实例可以赋值给A的对象。反之不成立,除非A和B定义的方法完全一样(顺序不要求),   
这时A和B等价,可以相互赋值。)

接口继承 ---- 接口转换

type  aa  interface{  //子集  可以理解父类
     run ()
}
type  bb interface{   //超集 可以理解子类
      aa // 匿名字段,继承run()
      say()
}
//超集可以转换为子集,反过来不可以
var  iPro bb  // 超集
var  i  aa   // 子集
i = iPro  // 可以
iPro = i // 不可以

空接口(万能类型,可以保存任意类型的值)

var i interface{} = 1
i = "abc"
i = true // 不会报错
func aa(args ...interface{}){
  // 可以接收任意数量,任意类型的参数
}

类型断言--if 和switch 都可以实现

if实现
type Student struct{
    Name string
}
func main(){
    //  set a slice typeof is everything,length is three
    slice := make([]interface{},3)  
    slice[0] = 1   //typeof is int
    slice[1] = "xxx"  // typeof is string
    slice[2] = Student{"names"} // typeof is Student
    // so we back to assert the typeof in slice everyone
    for index, value :range slice{ // value is slice[0],slice[1]...
        //assert
        if data,ok :=value.(int);ok == true{
            //this typeof is int
        }else if data,ok :=value.(string);ok == true{
            //this typeof is string
        }else if data,ok :=value.(Student);ok == true{
            //this typeof is Student
        }
    }
}
switch实现(替换调if判断就ok)
switch data := value.(type){
    case int:
        //type is int
    case string:
        //type is string
    case Student:
        //type is Student
}

你可能感兴趣的:(Go基础编程---面向对象)