Golang 面向对象

1. 简介

  • go 没有面向对象语法等的要求
  • go 语言对于面向对象的设计非常简洁而优雅
  • 没有封装(让数据更加安全,例如年龄不能是负的)、继承(减少代码冗余,父类和子类)、多态(可以产出不同的实例)这些概念,但同样通过别的方式实现这些特性
    • 封装:通过方法实现
    • 继承:通过匿名字段实现
    • 多态:通过接口实现

2. 匿名字段

go 结构体中支持只提供类型而不写字段名的方式,也就是匿名字段,也称为嵌入字段

package main

import "fmt"

// 定义一个存储人的结构体
type Person struct {
    name string
    sex  int
    age  int
}

// 定义一个存储学生的结构体,因为学生是是具有人结构体中的字段特性
// 不需要重新声明,直接继承人这个结构体的字段就可以
// 不能存在多个相同的匿名字段
type Student struct {
    Person //   继承人结构体, 匿名字段
    id      int
    address string
}

func main() {
    // 初始化
    stu01 := Student{Person{"zs", 0, 18}, 1, "bj"}
    fmt.Println(stu01)

    stu02 := Student{id: 1, address: "bj"}
    fmt.Println(stu02)

    stu03 := Student{Person: Person{name:"ls", sex:0, age:1}}
    fmt.Println(stu03)

    // 取值
    // 取Person的name (如果子结构体没有和父结构体同名字段,以下两种方式都可以)
    fmt.Println(stu01.name)
    fmt.Println(stu01.Person.name)

    fmt.Println(stu01.id)
}

同名字段的情况

package main

import "fmt"

// 定义一个存储人的结构体
type Person struct {
    name string
    sex  int
    age  int
}

// 定义一个存储学生的结构体,因为学生是是具有人结构体中的字段特性
// 不需要重新声明,直接继承人这个结构体的字段就可以
type Student struct {
    Person //   继承人结构体, 匿名字段
    id      int
    address string

    // 同名字段
    name string
}

func main() {
    var stu Student
    // 给自己的name字段赋值
    stu.name = "zs"

    // 给父的name字段进行赋值
    stu.Person.name = "ls"

    // 取自己的name字段的值(就近原则,不会取到父的name)
    fmt.Println(stu.name)

    // 取父的name字段的值
    fmt.Println(stu.Person.name)
}

所有的内置类型和自定义类型都是可以作为匿名字段去使用

package main

import "fmt"

// 自定义一个类型
type myint int

type student struct {
    int     // 内置类型匿名字段
    myint   // 自定义类型匿名字段
}

func main() {
    // 初始化
    s := student{1,2}

    // 取值
    fmt.Println(s.int)

    fmt.Println(s.myint)
}

指针类型匿名字段

package main

import "fmt"

type Person struct {
    name string
    age int
    sex int
}

type Student struct {
    *Person
    id int
    addr string
}

func main() {
    stu := Student{&Person{"zs", 18, 1}, 3, "bj"}
    fmt.Println(stu)
    fmt.Println(stu.name)
    fmt.Println(stu.Person.name)
    fmt.Println(stu.id)
}

3. 方法

  • 在面向对象编程中,一个对象其实也就是一个简单的值或者一个变量,在这个对象中会包含一些函数
  • 这种带有接收者的函数,我们称为方法,本质上,一个方法则是一个和特殊类型关联的函数
  • 方法的语法如下 func (接收参数名 接收类型) 方法名(参数列表)(返回值)
  • 可以给任意自定义类型(包括内置类型,但不包括指针类型)添加相应的方法

基础类型作为接收者

package main

import "fmt"

type myint int

// 面向过程思维写法
func add(a, b myint) myint {
    return a + b
}

// 面向对象接口思维写法
func (a myint) add(b myint) myint {
    return a + b
}

func main() {
    var a myint = 1
    var b myint = 2

    // 面向过程
    c := add(a,b)
    fmt.Println(c)

    // 面向思维
    d := a.add(b)
    fmt.Println(d)
}

结构体作为接收者

package main

import "fmt"

type Person struct {
    name string
    age  int
    sex  int
}

func (p Person) print() {
    fmt.Println(p.name, p.age, p.sex)
}

func main() {
    p := Person{"zs", 18, -0}
    p.print()
}

值语义和引用语义

package main

import "fmt"

type Person struct {
    name string
    age  int
    sex  int
}

// 引用语义
func (p *Person) setValue001() {
    p.name = "123"
}

// 值语义
func (p Person) setValue002() {
    p.name = "abc"
}

func main() {
    p := Person{"zs", 18, 0}
    fmt.Println(p)

    // 值语义, 不会改变原结构体
    p.setValue002()
    fmt.Println("值语义",p)

    // 引用语义,会改变原结构体
    p.setValue001()
    fmt.Println("引用语义",p)
}

方法继承

package main

import "fmt"

type Person struct {
    name string
    age  int
    sex  int
}

type Student struct {
    Person
    id   int
    addr string
}

func (p *Person) printInfo() {
    fmt.Println("person", p.name, p.age, p.sex)
}

func main() {
    p := Person{"zs", 18, 1}
    p.printInfo()

    s := Student{Person{"ls", 22, 0}, 1, "bj"}
    // 子调用父继承来的方法
    s.printInfo()
}

方法重写

package main

import "fmt"

type Person struct {
    name string
    age  int
    sex  int
}

func (p *Person) printInfo() {
    fmt.Println("person", p.name, p.age, p.sex)
}

type Student struct {
    Person
    id   int
    addr string
}

// 重写父的printInfo方法
func (s *Student) printInfo() {
    fmt.Println("student", s.name, s.age, s.sex, s.id, s.addr)
}

func main() {
    p := Person{"zs", 18, 1}
    p.printInfo()

    s := Student{Person{"ls", 22, 0}, 1, "bj"}
    // 子调用重写父来的方法
    s.printInfo()
}

方法值和方法表达式

package main

import "fmt"

type Person struct {
    name string
    age  int
    sex  int
}

func (p *Person) printInfo() {
    fmt.Println("person", p.name, p.age, p.sex)
}

func main() {
    p := Person{"zs", 18, 1}
    p.printInfo()

    // 方法值
    pFunc01 := p.printInfo
    pFunc01()

    // 方法表达式
    pFunc02 := (*Person).printInfo
    pFunc02(&p)
}

1. 包和封装

  • 方法首写字母大写可以被外部包调用
  • 方法首写字母小写是私有方法只能在当前包内调用
  • 为结构体定义的方法必须放在同一个包内,可以是不同的文件

5. init 函数以及执行顺序

一个包里可以有 0 个或多个 init 函数,在程序启动时自动调用

package main

import "fmt"

var a int = 10
var b int = 20

func init() {
    fmt.Println("a: ", a)
    fmt.Println("init01")
}

func init() {
    fmt.Println("b: ", b)
    fmt.Println("init02")
}

func main() {
    fmt.Println("main")
}

go 程序初始化顺序

main包 --> import --> 全局const --> 全局var --> init()  --> main()

如果一个 main 包引入了别的包,初始化顺序是先初始化被引用的包

6. 接口

  • go 语言中,接口(interface)是一个自定义类型,描述了一系列方法的集合
  • 接口不能被实例化

  • 接口定义语法 type 接口名 interface{}
    • ps: 接口命名习惯以er结尾

接口的定义与实现

package main

import "fmt"

// 定义一次接口
type Humaner interface {
    // 定义接口的方法
    Say()
}

// 定义学生信息结构体
type Student struct {
    name  string
    age   int
    score float64
}

// Student结构体实现Humaner接口
func (stu *Student) Say() {
    fmt.Println("学生...: ", stu.name)
}

type Teacher struct {
    name    string
    age     int
    subject string
}

func (tea *Teacher) Say() {
    fmt.Println("老师...: ", tea.name)
}

type myString string

func (mystr myString) Say() {
    fmt.Println("myString...: ", mystr)
}

// 定义一个接收Humaner接口类型参数的统一函数
func WhoSay(s Humaner) {
    s.Say()
}

func main() {
    stu01 := &Student{"zs", 18, 99.8}
    tea01 := &Teacher{"ls", 20, "计算机"}
    var mystr myString = "hello"

    // 各自调用自己的方法
    stu01.Say()
    tea01.Say()
    mystr.Say()

    // 使用统一接口调用
    WhoSay(stu01)
    WhoSay(tea01)
    WhoSay(mystr)

    // 已经实现接口的类型也可以是那个接口类型
    x := make([]Humaner, 3)
    x[0], x[1], x[2] = stu01, tea01, mystr
    for _, v := range x {
        v.Say()
    }
}

接口继承

package main

import "fmt"

// 定义一次接口
type Humaner interface {
    // 定义接口的方法
    Say()
}

// 定义继承Humaner的接口
type Personer interface {
    // 相当于写了Say()
    Humaner
    Sing(lyrics string)
}

// 定义学生信息结构体
type Student struct {
    name  string
    age   int
    score float64
}

// Student结构体实现Humaner接口
func (stu *Student) Say() {
    fmt.Println("学生...: ", stu.name)
}

func (stu *Student) Sing(lyrics string) {
    fmt.Println(lyrics)
}


// 定义一个接收Humaner接口类型参数的统一函数
func WhoSay(s Humaner) {
    s.Say()
}

func main() {
    stu01 := &Student{"zs", 18, 99.8}
    var p Personer
    // 结构体对象赋值给接口对象
    p = stu01
    // 接口调用方法
    p.Say()
    p.Sing("hello world")
}

接口类型变量

可以存储任何实现了该接口所有方法的对象类型

package main

import "fmt"

type Animal interface {
    Talk()
    Eat()
    Name() string
}

type Dog struct {
    name string
}

func (d *Dog) Talk() {
    fmt.Println("汪汪汪...")
}

func (d *Dog) Eat() {
    fmt.Println("吃狗x")
}

func (d *Dog) Name() string {
    fmt.Println(d.name)
    return d.name
}

type Pig struct {
    name string
}

func (p *Pig) Talk() {
    fmt.Println("哼哼哼...")
}

func (p *Pig) Eat() {
    fmt.Println("吃猪x")
}

func (p *Pig) Name() string {
    fmt.Println(p.name)
    return p.name
}

func main() {
    // 创建两个实现了Animal 接口的对象
    dog01 := &Dog{"旺财"}
    pid01 := &Pig{"佩奇"}

    // 定义接口类型变量
    var a Animal

    // 可以直接将实现了Animal 接口的对象赋值给Animal接口类型
    a = dog01
    a.Eat()
    a.Name()
    a.Talk()

    a = pid01
    a.Eat()
    a.Name()
    a.Talk()
}

接口类型和指针类型

值类型实现接口,指针类型可以存进去; 但指针类型实现接口,值类型存不进去

值类型实现接口,指针类型可以存进去

package main

import "fmt"

type Animal interface {
    Talk()
    Eat()
    Name() string
}

type Dog struct {
    name string
}

func (d Dog) Talk() {
    fmt.Println("汪汪汪...")
}

func (d Dog) Eat() {
    fmt.Println("吃狗x")
}

func (d Dog) Name() string {
    fmt.Println(d.name)
    return d.name
}

func main() {
    var dog01 *Dog = &Dog{"旺财"}

    var a Animal

    a = dog01
    a.Eat()
    a.Name()
    a.Talk()
}

指针类型实现接口,值类型存不进去

package main

import "fmt"

type Animal interface {
    Talk()
    Eat()
    Name() string
}

type Dog struct {
    name string
}

func (d *Dog) Talk() {
    fmt.Println("汪汪汪...")
}

func (d *Dog) Eat() {
    fmt.Println("吃狗x")
}

func (d *Dog) Name() string {
    fmt.Println(d.name)
    return d.name
}

func main() {
    var dog01 Dog
    var a Animal

    // 若类变量存储在接口变量中
    // 若传值类型,不能获取变量地址,d取不到地址
    // 寻址问题不通过
    // 编译不通过
    a = dog01
}

同一个类型可以实现多个接口

package main

import "fmt"

type Animal interface {
    Talk()
    Eat()
    Name() string
}

type Animal2 interface {
    Run()
}

type Dog struct {
    name string
}

func (d *Dog) Talk() {
    fmt.Println("汪汪汪...")
}

func (d *Dog) Eat() {
    fmt.Println("吃狗x")
}

func (d *Dog) Name() string {
    fmt.Println(d.name)
    return d.name
}

// Dog结构体实现第二个接口
func (d *Dog) Run() {
    fmt.Println("run")
}

func main() {
    dog01 := &Dog{}
    var a01 Animal
    var a02 Animal2
    a01 = dog01
    a02 = dog01

    a01.Talk()
    a02.Run()
}

接口是可以嵌套的

package main

type Animal interface {
    Talk()
    Eat()
    Name() string
}

type Animal2 interface {
    Run()
}

// 继承前两个接口
type Animal3 interface {
    Animal
    Animal2
}

func main() {
}

空接口

args ...interface{}

func Println(a ...interface{}) (n int, err error) {
    return Fprintln(os.Stdout, a...)
}

类型查询

  • comma-ok 断言
  • switch测试

comma-ok 断言

package main

//comma-ok断言

import "fmt"

type Person struct {
    name string
    age int
}

func main() {
    // 定义空接口切片
    t := make([]interface{}, 3)
    t[0] = 1
    t[1] = "hello"
    t[2] = &Person{}

    // 类型断言: value,ok := 元素.(Type)
    // value是变量值,ok是布尔,是不是这个类型
    for i, v := range t {
        if value, ok := v.(int); ok {
            fmt.Printf("index[%d]是int类型, value: %d\n", i, value)
        }else if value, ok := v.(string); ok {
            fmt.Printf("index[%d]是string类型, value: %s\n", i, value)
        } else {
            fmt.Printf("其他类型\n")
        }
    }
}

switch 测试

package main

import "fmt"

type Person struct {
    name string
    age  int
}

func main() {
    // 定义空接口切片
    t := make([]interface{}, 3)
    t[0] = 1
    t[1] = "hello"
    t[2] = &Person{}

    for i, v := range t {
        switch value := v.(type) {
        case int:
            fmt.Printf("index[%d]是int类型, value: %d\n", i, value)
        case string:
            fmt.Printf("index[%d]是string类型, value: %s\n", i, value)
        default:
            fmt.Printf("其他类型\n")
        }
    }
}

猜你喜欢

转载自www.cnblogs.com/zhichaoma/p/12510004.html