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")
		}
	}
}
posted @ 2020-03-17 13:15  ZhiChao&  阅读(397)  评论(0编辑  收藏  举报