go-面向对象

go-面向对象编程-上

面向对象的特点

  1. golang支持面向对象编程(OOP),但是和传统的面向对象编程有区别,并不是纯粹的面向对象语言。
  2. golang没有类(class),golang的结构图(struct)和其他编程语言的类(class)有同等的地位,可以理解为golang是基于struct来实现OOP特性
  3. golang面向对象编程非常简介,去掉了传统oop语言的继承、方法重载、构造函数和析构函数、隐藏的this指针等等
  4. golang仍然有面向对象编程的继承、封装和多态的特性,只是实现的方式和其他OOP语言不一样
  5. golang面向对象(OOP)很优雅,OOP本身是语言类型系统的一部分,通过接口(interface)关联,耦合性低,非常灵活。

struct结构体

type Cat struct {
	Name  string
	Age   int
	Color string
}

func main() {
	var cat1 Cat = Cat{"tom", 3, "withe"}
	fmt.Printf("cat1: %v\n", cat1) // cat1: {tom 3 withe}

	cat1.Name = "xiaobai"
	fmt.Printf("cat1.Name: %v\n", cat1.Name)   // cat1.Name
	fmt.Printf("cat1.Age: %v\n", cat1.Age)     // cat1.Age: 3
	fmt.Printf("cat1.Color: %v\n", cat1.Color) // cat1.Color: withe
}

内存布局

  1. 结构图属于值类型
  2. 声明时就给定了内存空间,且有默认值

image-20220426221829125

结构体的使用

  1. 字段 = 属性 = field
  2. 字段是结构体的一个组成部分,一般是基本数据类型、数组、也可以是引用类型
  3. 字段声明语法同遍历一样,字段名 字段类型
  4. 字段的类型可以为:基本类型、数组、引用类型
  5. 在创建一个结构体变量后,若没有给字段赋值,都对应一个默认值
  6. 不同结构体遍历的字段是独立的,互不影响,一个结构体遍历字段的更改,不影响另一个,结构体是值类型
  7. 当类型是指针、slice、map时,默认是nil,未分配空间。若要使用的话,需要先make,才能使用
func main() {
	var tem Temp
	tem.Name = "tem"

	tem.Array[1] = 1

	tem.Slice = make([]int, 3)
	tem.Slice[1] = 3

	age := 12
	tem.Ptr = &age

	tem.Map = make(map[string]int, 0)
	tem.Map["haha"] = 5
	tem.Map["xixi"] = 9

	fmt.Printf("tem: %v\n", tem)
	// tem: {tem [0 1 0] [0 3 0] 0xc00010c008 map[haha:5 xixi:9]}
}

type Temp struct {
	Name string

	Array [3]float64 // 数组

	Slice []int // 切片

	Ptr *int // 指针

	Map map[string]int // map
}
	var people1 People = People{"tom", 999}
	people2 := people1 // 值拷贝,值传递
	people2.Name = "jack"

	fmt.Printf("people1: %v\n", people1) // people1: {tom 999}
	fmt.Printf("people2: %v\n", people2) // people2: {jack 999}

type People struct {
	Name string
	Age  int
}

image-20220426225509789

结构体声明

type 结构体名称 struct{

field1 type

Field2 type // public

}

实例化-四种方式

  1. 第三种方法和第四种方法返回的是 结构体指针
  2. 结构体指针访问字段的标准方式是 (*结构体指针).字段名(*people).Name = "tom"
  3. golang做了简化,支持结构体指针.字段名 , people.Name = "tom"
func main() {
	// 1.
	var people1 People
	people1.Name = "jack"
	people1.Age = 11
	fmt.Printf("people1: %v\n", people1) // people1: {jack 11}

	// 2.
	var people2 People = People{"tome", 22}
	fmt.Printf("people2: %v\n", people2) // people2: {tome 22}

	// 3.
	var people3 *People = new(People)
  (*people3).Name = "mark" // 标准写法  <==> people3.Name
	people3.Name = "mark"
	people3.Age = 33
	fmt.Printf("people3: %v\n", people3) // people3: &{mark 33}
	people5 := people3
	people5.Age = 55
	people3.Name = "xxx"
	fmt.Printf("people3: %v\n", people3) // people3: &{xxx 55}
	fmt.Printf("people5: %v\n", people5) // people5: &{xxx 55}

	// 4.
	var people4 *People = &People{"nacy", 44}
	people6 := people4
	fmt.Printf("people4: %v\n", people4) // people4: &{nacy 44}
	people6.Age = 66
	fmt.Printf("people4: %v\n", people4) // people4: &{nacy 66}
	fmt.Printf("people6: %v\n", people6) // people6: &{nacy 66}

}

type People struct {
	Name string
	Age  int
}

结构体内存分配机制

type People struct {
	Name string
	Age  int
}

func main() {
	var p1 *People = new(People)
	p1.Name = "tom"
	p1.Age = 22

	p2 := p1
	p2.Name = "joun"
	fmt.Printf("p1: %v\n", p1) // p1: &{joun 22}
	fmt.Printf("p2: %v\n", p2) // p2: &{joun 22}

	fmt.Printf("p1的地址: %v\n", &p1) // p1的地址: 0xc0000ac018
	fmt.Printf("p2的地址: %v\n", &p2) // p2的地址: 0xc0000ac020
	fmt.Printf("p2: %p\n", p2)     // p2: 0xc0000a4018

}

image-20220427180049053

结构体使用细节

  1. 结构体中的所有字段在内存中是连续的
type Point struct {
	x int
	y int
}

type Rect struct {
	leftUp, rightDown Point
}

type Rect2 struct {
	leftUp, rightDown *Point
}

func main() {

	r1 := Rect{Point{1, 2}, Point{3, 4}}
	fmt.Printf("r1: %v\n", r1) // r1: {{1 2} {3 4}}

	// 1. 内存分布连续
	fmt.Printf("r1.leftUP.x地址 = %p \n", &r1.leftUp.x)       // r1.leftUP.x地址 = 0xc00012e000 +8
	fmt.Printf("r1.leftUP.y地址 = %p \n", &r1.leftUp.y)       // r1.leftUP.y地址 = 0xc00012e008 +8
	fmt.Printf("r1.rightDown.x地址 = %p \n", &r1.rightDown.x) // r1.rightDown.x地址 = 0xc00012e010 +8
	fmt.Printf("r1.rightDown.y地址 = %p \n", &r1.rightDown.y) // r1.rightDown.y地址 = 0xc00012e018

	// 2. r2有两个 *Point类型,这两个*point类型的本身地址也是连续的
	// 		但是他们所指向的地址不一定连续,取决于系统的执行
	r2 := Rect2{&Point{11, 22}, &Point{33, 44}}
	fmt.Printf("r2: %v\n", r2) // r2: {0xc0000160c0 0xc0000160d0}

	fmt.Printf("r2.leftUP地址 = %p \n", &r2.leftUp)       // r2.leftUP地址 = 0xc000010250
	fmt.Printf("r2.rightDown地址 = %p \n", &r2.rightDown) // r2.rightDown地址 = 0xc000010258

	fmt.Printf("r2.leftUP指向的地址 = %p \n", r2.leftUp)       // r2.leftUP指向的地址 = 0xc0000160c0 + 16
	fmt.Printf("r2.rightDown指向的地址 = %p \n", r2.rightDown) // r2.rightDown指向的地址 = 0xc0000160d0

	fmt.Printf("r2.leftUP.x地址 = %p \n", &r2.leftUp.x)       // r2.leftUP.x地址 = 0xc0000160c0 +8
	fmt.Printf("r2.leftUP.y地址 = %p \n", &r2.leftUp.y)       // r2.leftUP.y地址 = 0xc0000160c8 +8
	fmt.Printf("r2.rightDown.x地址 = %p \n", &r2.rightDown.x) // r2.rightDown.x地址 = 0xc0000160d0  +8
	fmt.Printf("r2.rightDown.y地址 = %p \n", &r2.rightDown.y) // r2.rightDown.y地址 = 0xc0000160d8
}
  1. 结构体是用户单独定义的类型,和其他类型进行转换时,需要有完全相同的字段(名字、个数、类型)
func main() {
	var a A = A{11}
	var b B
	fmt.Printf("a: %v\n", a) // a: {11}
	fmt.Printf("b: %v\n", b) // b: {0}
	b = B(a)
	fmt.Printf("b: %v\n", b) // b: {11}
}

type A struct {
	num int
}
type B struct {
	num int
}
  1. 结构体进行type重新定义(相当于取别名),Golang认为是新的数据类型,但是相互间可以进行强转
func main() {

	var stu1 Student = Student{"tom"}
	var stu2 Stu
	stu2 = Stu(stu1)               // struct 类型需要强转
	fmt.Printf("stu2: %v\n", stu2) // stu2: {tom}

	var a int = 1
	var b Integer
	b = Integer(a)           // 基本数据类型也需要强转
	fmt.Printf("b: %v\n", b) //b: 1
}

type Student struct {
	Name string
}

type Stu Student

type Integer int

  1. struct的每个字段上,可以写一个tag,该tag可以通过反射机制获取,常见的使用场景就是序列化和反序列化
type Monster struct {
	Name  string `json:"name"`
	Age   int    `json:"age"`
	Skill string `json:"skill"`
}

func main() {
	// 1. 创建一个Monster变量
	monster := Monster{"niumowang", 505, "bajiaoshan"}
	fmt.Printf("monster: %v\n", monster)

	// 2. 将monster序列化为json字符串
	//    json.Marshal 函数中使用了反射机制
	data, err := json.Marshal(monster)
	if err == nil {
		fmt.Printf("data: %c\n", data)
		// data: [{ " n a m e " : " n i u m o w a n g " , " a g e " : 5 0 5 , " s k i l l " : " b a j i a o s h a n " }]
		fmt.Printf("string(data): %v\n", string(data)) // []byte --> string
		// string(data): {"name":"niumowang","age":505,"skill":"bajiaoshan"}
	} else {
		fmt.Printf("err: %v\n", err)
	}
}

方法

  1. golang中的方法是作用在指定的数据类型上,因此自定义类型,都可以有方法,而不仅仅是struct
  2. func ([结构体变量] 结构体) 方法名(参数) 返回值 { }

方法的使用

func main() {
	var people People = People{"tom", 22}
	fmt.Printf("people: %v\n", people)            // people: {tom 22}
	people.say()                                  // 调用say hahaha \n p.Name: tom
	res1, res2 := people.run(10, 20)              // 调用run
	fmt.Printf("res1: %v res2: %v\n", res1, res2) // res1: 30 res2: -10
	people.UpdateName("jack")
	fmt.Printf("people.Name: %v\n", people.Name) // people.Name: tom
	(*&people).UpdateAge(33)
	fmt.Printf("people.Age: %v\n", people.Age) // people.Age: 33
	people.UpdateAge(55)
	fmt.Printf("people.Age: %v\n", people.Age) // people.Age: 55
}

type People struct {
	Name string
	Age  int
}

func (p People) say() {
	fmt.Println("hahaha")
	fmt.Printf("p.Name: %v\n", p.Name) // 使用结构体中的字段
}

func (p People) run(a, b int) (int, int) {
	return a + b, a - b
}

func (p People) UpdateName(name string) {
	p.Name = name
	fmt.Printf("p.Name: %v\n", p.Name) // p.Name: jack
}

func (p *People) UpdateAge(age int) {
	p.Age = age
}

方法调用和传参机制

  1. 在通过一个变量去调用方法时,其调用机制和函数一样
  2. 变量调用方法时,该变量本身也会作为一个参数传递到方法(若变量是值类型,则是值拷贝;若是引用类型,则是地址拷贝)

image-20220427194727014

func main() {
	var circle Circle
	circle.radius = 1
	area := circle.area()
	fmt.Printf("area: %v\n", area) // area: 3.141592653589793
}

type Circle struct {
	radius float64
}

func (circle Circle) area() float64 {
	res := math.Pow(circle.radius, 2.0) * math.Pi
	return res
}

方法的声明

func (recevier type) methodName(参数列表) (返回值列表) {

方法体

return 返回值

}

  1. 参数列表:表示方法的输入
  2. recevier type: 便是这个方法和type这个类型进行绑定,或者说该方法作用于type类型
  3. recevier tye: type可以是结构体,也可以是其他的自定义类型
  4. recevier:就是type类型的一个变量
  5. 返回值列表:便是返回的值,可以是多个
  6. 方法主体:表示为了实现某一功能代码块
  7. return语句不是必须的

注意细节

  1. 结构体类型是值类型,在方法调用中,遵守值类型的传递机制,是值拷贝传递方式

  2. 若希望在方法中,修改结构体变量的值,可以通过结构体指针的方式来处理

  3. golang中的方法作业再指定的数据类型上的,因此自定义类型,都可以都方法,而不仅仅是struct,比如int,float32

    func main() {
    	var a Interge
    	a.hello()
    }
    
    type Interge int
    
    func (integer Interge) hello() {
    	fmt.Println("hello int")
    }
    
  4. 方法的访问范围控制的规则和函数一样。方法名首字母小写,只能在本包访问,方法首字母大写,可以在本包和其他包中访问

  5. 若一个变量实现了String() 方法,那么fmt.Ptintln默认会调用这个变量的String()进行输出

    func main() {
    	stu := Student{
    		Name: "tom",
    		Age:  22,
    	}
    	fmt.Println(&stu) // Name = [tom]  Age = [22]
    }
    
    type Student struct {
    	Name string
    	Age  int
    }
    
    func (stu *Student) String() string {
    	str := fmt.Sprintf("Name = [%v]  Age = [%v]", stu.Name, stu.Age)
    	return str
    }
    

方法和函数的区别

  1. 调用方式不一样

    函数的调用方式:函数名(实参列表)

    方法的调用方式:变量.方法名(实参列表)

  2. 对于普通函数,接受者为值类型,不能将指针类型的数据直接传递,,反之亦然

  3. 对于方法,接受者为值类型是,可以只用用指针类型的变量调用方法,反过来同样也可以

  4. 真正决定是值拷贝还是地址拷贝的是,方法本身的类型决定

    若方法本身是值类型,则是值拷贝。若方法本身是引用类型,则是地址拷贝

func main() {
	var p People = People{"tom", 22}
	test1(p)
	fmt.Printf("p: %v\n", p) // p: {tom 22}
	test2(&p)
	fmt.Printf("p: %v\n", p) // p: {tom 22}
	p.test3()
	(&p).test3()             // 这个也不会改变name,仍是值传递
	fmt.Printf("p: %v\n", p) // p: {tom 22}
	p.test4()                // 这个看似是值传递,实则是引用传递
	(&p).test4()
	fmt.Printf("p: %v\n", p) // p: {mary 22}
}

type People struct {
	name string
	age  int
}

func test1(p People) {
	fmt.Printf("p.name: %v\n", p.name) // p.name: tom
}

func test2(p *People) {
	fmt.Printf("p.name: %v\n", p.name) // p.name: tom
}

func (p People) test3() {
	p.name = "jack"
	fmt.Printf("p.name: %v\n", p.name) // p.name: jack
}

func (p *People) test4() {
	p.name = "mary"
	fmt.Printf("p.name: %v\n", p.name) // p.name: mary
}

工厂模式

model
student.go

type Student struct {
	Name string
	Age  int
}

func (student *Student) GetSum(a, b int) int {
	return a + b
}

// people 首字母未大写, 因此需要使用工厂模式
type people struct {
	Name string
	age  int
}

// 工厂模式
func NewPeople(name string, age int) *people {
	people := people{name, age}
	return &people
}

func (p *people) SetAge(age int) {
	p.age = age
}


model
main.go

import (
	"fmt"
	"go_code/Study_go/Study_go_nine/demo17/model"
)

func main() {
	var student model.Student = model.Student{
		Age:  20,
		Name: "tom",
	}

	fmt.Printf("student: %v\n", student)                               // student: {tom 20}
	fmt.Printf("student.GetSum(20, 30): %v\n", student.GetSum(20, 30)) // student.GetSum(20, 30): 50

	// 工厂模式 -- 类似于 java中的构造函数
	p := model.NewPeople("jack", 22) // p是引用类型 指针
	// p.age = 20  不能访问age
	fmt.Printf("p: %v\n", p) // p: &{jack 22}
	p.Name = "xixi"
	fmt.Printf("p: %v\n", p)  // p: &{xixi 22}
	fmt.Printf("p: %v\n", *p) // p: {xixi 22}
	p.SetAge(33)
	fmt.Printf("p: %v\n", *p) // p: {xixi 33}
}

go-面向对象编程-下

抽象/封装

认识

type Account struct {
	AccountNo string
	Pwd       string
	Balance   float64
}

func (account *Account) Deposite(money float64, pwd string) {
	if account.Pwd == pwd {
		account.Balance += money
		fmt.Println("success")
	} else {
		fmt.Println("password error")
	}
}

func (account *Account) WithDraw(money float64, pwd string) {
	if pwd == account.Pwd {
		if money > account.Balance {
			fmt.Println("balance erro")
		} else {
			account.Balance -= money
			fmt.Println("success")
		}
	} else {
		fmt.Println("password error")
	}
}

func (account *Account) Query(pwd string) {
	if pwd == account.Pwd {
		fmt.Printf("account.Balance: %v\n", account.Balance)
	} else {
		fmt.Println("password error")
	}
}


import "go_code/Study_go/Study_go_ten/demo01/main/pojo"

func main() {
	var account pojo.Account = pojo.Account{
		AccountNo: "123123123",
		Pwd:       "123123",
		Balance:   50.0,
	}
	account.Query("123123")        // account.Balance: 50
	account.Deposite(50, "123123") // success
	account.Query("123123")        // account.Balance: 100
	account.WithDraw(80, "123123") // success
	account.Query("123123")        // account.Balance: 20
}

封装

  1. 封装就是把抽象出来的字段和对字段的操作封装在一起,数据被保护在内部,程序的其他包只有通过被授权的操作(方法),才能对字段进行操作

  2. 好处:隐藏实现细节,可以对数据进行验证,保证安全合理

  3. 封装实现步骤:

    1. 结构体、字段的首字母小写

    2. 给结构体所在包提供一个工厂模式的函数,首字母大写,类似一个构造函数

    3. 提供一个首字母大写的Set方法,用于对属性判断并赋值

      func (var 结构体类型名) SetXxx(参数列表)(返回值列表) {

      var.字段 = 参数

      }

    4. 提供一个首字母大写Get方法,用于获取属性的值

      func (var 结构体类型名) GetXxx() 返回值类型 {

      return var.字段

      }

import (
	"fmt"
	"go_code/Study_go/Study_go_ten/demo02/pojo"
)

func main() {
	person := pojo.NewPerson("tom", 22, 11231.2)
	fmt.Printf("person: %v\n", person) // person: &{tom 22 11231.2}

	person.SetAge(33)
	person.SetSal(23321.2)
	fmt.Printf("person.GetAge(): %v\n", person.GetAge()) // person.GetAge(): 33
	fmt.Printf("person.GetSal(): %v\n", person.GetSal()) // person.GetSal(): 23321.2
}


type person struct {
	name string
	age  int
	sal  float64
}

// 工厂模式
func NewPerson(name string, age int, sal float64) *person {
	person := person{}
	person.name = name
	person.age = age
	person.sal = sal
	return &person
}

func (p *person) SetAge(age int) {
	p.age = age
}
func (p *person) GetAge() int {
	return p.age
}
func (p *person) SetSal(sal float64) {
	p.sal = sal
}
func (p *person) GetSal() float64 {
	return p.sal
}

继承

  1. 在golang中,若一个struct嵌套了另一个匿名结构体,那么这个结构体可以直接访问匿名结构体的字段和方法,从而实现了继承特性

  2. 基本语法

    基本语法:
    type Goods struct {
      	Name string
      	Price int
    }
    type Book struct {
      	Goods // 这里就是嵌套匿名结构体
      	Writer string
    }
    
    
    // 举例说明
    type Student struct {
    	Name  string
    	Age   int
    	Score float64
    }
    
    func (stu *Student) showInfo() {
    	fmt.Printf("stu: %v\n", stu)
    }
    
    func (stu *Student) setScore(score float64) {
    	stu.Score = score
    }
    
    type Pupil struct {
    	Student // 嵌入匿名结构体Student
    }
    
    func (p *Pupil) testing() {
    	fmt.Println("pupil testing ...")
    }
    
    type Graduate struct {
    	Student // 嵌入匿名结构体Student
    }
    
    func (graduate *Graduate) testing() {
    	fmt.Println("graduate testing ...")
    }
    
    func main() {
    
    	var pupil *Pupil = &Pupil{}
    	pupil.Name = "tom"
    	pupil.Age = 11
    	pupil.testing()
    	pupil.setScore(90)
    	pupil.showInfo() // stu: &{tom 11 90}
    
    }
    
  3. 结构体可以使用嵌套匿名结构体所有的字段和方法,即:首字母大写或小写的字段、方法,都可以使用

  4. 匿名结构体字段访问可简化a.A.Name = xx ==> a.Name = xx

  5. 当结构体和匿名结构体有相同的字段或者是方法时,编译器采用就近访问原则访问,如希望访问匿名结构体的字段和方法,可以通过匿名结构体名来区分b.B.Hello() 访问匿名结构体的方法b.Hello() 访问本结构体的方法

package main

import "fmt"

type A struct {
	name string
	Age  int
}

func (a *A) sayhello() {
	fmt.Println("A hello")
}

func (a *A) setAge() {
	a.Age = 10
	fmt.Printf("a.Age: %v\n", a.Age)
}

type B struct {
	A
	Age int
}

func (b *B) sayhello() {
	fmt.Println("B hello")
}

func main() {
	var b B
	b.A.name = "tom"
	b.name = "jack"          // 简化调用
	b.A.Age = 12             // 就近调用
	b.Age = 22               // 就近调用
	b.sayhello()             // B hello
	b.A.sayhello()           // A hello
	fmt.Printf("b: %v\n", b) // b: {{jack 12} 22}
}

  1. 多继承:结构体嵌入两个(或多个)匿名结构体,如两个匿名结构体有相同的字段和方法(同时结构体本身没有同名的字段和方法),在访问时,就必须明确指定匿名结构体名字,否则编译报错。

    1. 若嵌入的匿名结构体有相同的字段名或者方法名,则在访问时,需要通过匿名结构体类型来区分
    2. 为了保证代码的简洁性,建议不使用多继承
    type A struct {
    	name string
    	age  int
    }
    
    func (a *A) say() {
    	fmt.Println("AAAAA")
    }
    
    type B struct {
    	name string
    	age  int
    }
    
    func (b *B) say() {
    	fmt.Println("BBBBB")
    }
    
    type C struct {
    	A
    	B
    }
    
    func main() {
    	var c C
    	c.A.name = "tom"
    	c.B.name = "jack"
    	c.A.age = 11
    	c.B.age = 22
    	fmt.Printf("c: %v\n", c) // c: {{tom 11} {jack 22}}
    	c.A.say()                // AAAAA
    	c.B.say()                // BBBBB
    }
    
  2. 若一个struct嵌套了一个有名结构体,这种模式就是组合模式,若是组合关系,那么在访问组合的结构体的字段或方法时,必须带上结构体的名字

    type A struct {
    	name string
    	age  int
    }
    
    func (a *A) say() {
    	fmt.Println("hello aaaaa")
    }
    
    type B struct {
    	a A
    }
    
    func main() {
    	var b B
    	b.a.name = "tom"
    	b.a.age = 11
    	fmt.Printf("b: %v\n", b) // b: {{tom 11}}
    	b.a.say()                // hello aaaaa
    }
    
  3. 嵌套匿名结构体后,也可以在创建结构体变量时,直接指定各个匿名结构体的字段值

    type Goods struct {
    	Name  string
    	Price float64
    }
    
    type Brand struct {
    	Name    string
    	Address string
    }
    
    type TV struct {
    	*Goods
    	*Brand
    }
    
    func main() {
    	tv := TV{
    		Goods: &Goods{
    			Name:  "TV",
    			Price: 2999.99,
    		},
    		Brand: &Brand{
    			Name:    "meidi",
    			Address: "beijing",
    		},
    	}
    	fmt.Printf("tv: %v\n", tv) // tv: {{TV 2999.99} {meidi beijing}}
    	// tv: {0xc0000a4018 0xc0000b6000}
    
    	tv1 := &TV{&Goods{"air-d", 3999.99}, &Brand{"haier", "shanghai"}}
    	fmt.Printf("tv1: %v\n", tv1)             // tv1: &{0xc00000c048 0xc000060040}
    	fmt.Printf("tv1: %v\n", *tv1)            // tv1: {0xc00000c048 0xc000060040}
    	fmt.Printf("tv1.Goods: %v\n", tv1.Goods) // tv1.Goods: &{air-d 3999.99
    	fmt.Printf("tv1.Brand: %v\n", tv1.Brand) // tv1.Brand: &{haier shanghai}
    }
    
    
  4. 基本数据类型也可以作为继承的匿名结构体

    1. 若一个结构体有int类型的匿名字段,就不能有第二个
    2. 若需要有多个int字段,则必须给int字段指定名字

接口

认识

type USB interface {
	Start()
	Stop()
}

type Phone struct {
}
type Carmer struct {
}

// 实现接口:只要是实现了结构的所有方法,就是实现了接口
func (p Phone) Start() {
	fmt.Println("phone start")
}
func (p Phone) Stop() {
	fmt.Println("phone stop")
}
func (c Carmer) Start() {
	fmt.Println("carmer start")
}
func (c Carmer) Stop() {
	fmt.Println("carmer stop")
}

type Computer struct {
}

// 体现多态:接收参数是接口结构,只要实现此结构的所有结构体都可作为参数
func (computer Computer) Working(usb USB) {
	usb.Start()
	usb.Stop()
}

func main() {
	computer := Computer{}
	phone := Phone{}
	carmer := Carmer{}
	computer.Working(phone)  // phone start  \n  phone stop
	computer.Working(carmer) // carmer start   \n carmer stop
}

介绍

  1. interface类型可以定义一组方法,但是这些不需要实现。并且interface不能包含任何变量

  2. 基本语法:

    type 接口名 interface {

    method1(参数列表) 返回值列表

    method2(参数列表) 返回值列表

    }

  3. 接口中的所有方法都没有方法体,即接口中的方法都是没有实现的方法。接口体现了程序设计的多态和高内聚低耦合的思想

  4. Golang中的接口,不需要显式的实现,只要一个变量,含有接口类型中的所有方法,那么这个变量就实现了这个接口。(不需要implement关键字)

注意事项和细节

  1. 接口本身不能实例化,但是可以指向一个实现了该接口的自定义类型的变量(实例)

  2. 接口中的所有方法都没有方法体,即都是没有实现的方法

  3. 在Golang中,一个自定义类型需要将某个接口的所有方法实现,即实现了这个接口

  4. 一个自定义类型只有实现了某个接口,才能将该自定义类型的实例赋给接口变量

  5. 只要是自定义类型,就可以实现接口,不仅仅是结构体类型

    type Person interface {
    	Say()
    }
    
    type Student struct {
    }
    
    func (stu Student) Say() {
    	fmt.Println("Student say")
    }
    
    type Integer int
    
    func (i Integer) Say() {
    	fmt.Println("Integer say i = ", i)
    }
    
    func main() {
    	var student Student
    	var person1 Person = student
    	person1.Say() // Student say
    
    	var i Integer = 10
    	var person2 Person = i
    	person2.Say() // Integer say i =  10
    }
    
    
  6. 一个自定义类型可以实现多个接口

  7. Golang接口中不能有变量

  8. 一个接口可以继承多个别的接口,这时若要实例化接口,就必须实现所有接口的方法

    type A interface {
    	SayA()
    	// name string  接口不能有变量
    }
    
    type B interface {
    	SayB()
    }
    
    type C struct {
    }
    
    func (c C) SayA() {
    	fmt.Println("c SayA")
    }
    
    func (c C) SayB() {
    	fmt.Println("c SayB")
    }
    func (c C) SayD() {
    	fmt.Println("c SayD")
    }
    
    type D interface { // 继承了接口A B
    	A
    	B
    	SayD()
    }
    
    func main() {
    	var c C
    	var a A = c
    	a.SayA() // c SayA
    
    	var b B = c
    	b.SayB() // c SayB
    
    	var d D = c
    	d.SayA() // c SayA
    	d.SayB() // c SayB
    	d.SayD() // c SayD
    }
    
    
  9. interface类型默认是一个指针,若没有对interface初始化使用,那么输出nil

  10. 空接口interface{} 没有任何方法,所以所有类型都实现了这个空接口

  11. 当接口继承其他多个接口时,这多个接口不能出现相同的方法,若出现了相同相同的方法,则编译出错,减少冗余

  12. 结构体实现方法时,绑定的结构体不能是*Student,因为Student 和 *Student不是一个类型

    type Person interface {
    	Say()
    }
    
    type Student struct {
    }
    
    func (student *Student) Say() {
    	fmt.Println("student say")
    }
    
    func main() {
    	var a Person
    	fmt.Printf("a: %v\n", a)  // a: <nil>
    	fmt.Printf("a: %p\n", &a) // a: 0xc000098210
    
    	var student Student
    	a = &student              // 注意:指针地址
    	fmt.Printf("a: %v\n", a)  // a: {}
    	fmt.Printf("a: %p\n", &a) // a: 0xc000098210
    	student.Say()             // student say
    	a.Say()                   // 指针调用:student say
    
    	var temp interface{} = student
    	fmt.Printf("temp: %v\n", temp) // temp: {}
    
    	var num int = 22
    	temp = num
    	fmt.Printf("temp: %v\n", temp) // temp: 22
    }
    

经典案例

import (
	"fmt"
	"sort"
)

type Hero struct {
	Name  string
	Age   int
	skill int
}

type HeroSlice []Hero

func (heros HeroSlice) Len() int           { return len(heros) }
func (heros HeroSlice) Swap(i, j int)      { heros[i], heros[j] = heros[j], heros[i] }
func (heros HeroSlice) Less(i, j int) bool { return heros[i].skill < heros[j].skill }

func main() {
	// 切片的基础排序
	var sliceNum []int = []int{2, 3, 5, 7, 1, 3}
	sliceNum = append(sliceNum, 500, 100)
	sort.Ints(sliceNum)
	fmt.Printf("sliceNum: %v\n", sliceNum) // sliceNum: [1 2 3 3 5 7 100 500]

	var heros HeroSlice
	heros = append(heros, Hero{"tom", 11, 1000})
	heros = append(heros, Hero{"jack", 15, 6000})
	heros = append(heros, Hero{"mary", 22, 3000})
	fmt.Printf("hero: %v\n", heros) // hero: [{tom 11 1000} {jack 15 6000} {mary 22 3000}]

	sort.Sort(heros)
	for _, v := range heros {
		fmt.Printf("v: %v\n", v)
		// v: {tom 11 1000}
		// v: {mary 22 3000}
		// v: {jack 15 6000}
	}

	// 内置切片排序
	sort.Slice(heros, func(i, j int) bool {
		return heros[i].Age < heros[j].Age
	})
	fmt.Printf("hero: %v\n", heros) // hero: [{tom 11 1000} {jack 15 6000} {mary 22 3000}]

}

接口与集成的比较

  1. 继承的价值主要在于:解决代码的复用性和可维护性
  2. 接口的价值在于:设计的规范性,设计好各种规范(方法),让其他自定义类型去实现这些方法
  3. 接口比继承更加灵活:继承是满足is-a的关系,而接口只需满足like-a的关系
  4. 接口一定程度上实现代码解耦
示例1
type Monkey struct {
	Name string
}

func (monkey *Monkey) climbing() {
	fmt.Println("monkey climbing")
}

type BirdAble interface {
	Flying()
}

type FishAble interface {
	Swimming()
}

type LitterMonkey struct {
	Monkey
}

func (litterMonkey *LitterMonkey) Flying() {
	fmt.Println("litterMonkey flying")
}

func (litterMonkey *LitterMonkey) Swimming() {
	fmt.Println("litterMonkey swimming")
}

func main() {
	monkey := LitterMonkey{
		Monkey{
			Name: "wukong",
		},
	}
	fmt.Printf("monkey: %v\n", monkey) // monkey: {{wukong}}
	monkey.climbing()                  // monkey climbing
	monkey.Flying()                    // litterMonkey flying
	monkey.Swimming()                  // litterMonkey swimming
}
示例2

image-20220502152334591

type Person struct {
	Name string
	Age  int
}

type Student struct {
	Person
	Score float64
}

type BigStudent struct {
	Student
	Professional string
}

type SmallStudent struct {
	Student
	Enjoy string
}

type Athlet struct {
	Person
	Address string
}

type BacketballAthlet struct {
	Athlet
	Team string
}

type SoccesAthlet struct {
	Athlet
	Guojia string
}

type English interface {
	Learn()
}

func (student *BigStudent) Learn() {
	fmt.Println("studet learn English")
}

func (athlet *BacketballAthlet) Learn() {
	fmt.Println("athlet learn English")
}

func main() {

}

多态

​ 类似接口中的USB

多态参数

​ 在前面的USB接口案例中,USB可以接收手机变量,也可以接收相机变量,就体现了USB接口的多态

多态数组

​ 给USB数组中,存放Phone结构体和Camera结构体变量,Phone还有一个特有的方法call(),请遍历USB数组,若是Phone变量,除了调用USB接口声明的方法外,还需要调用Phone 特有的方法call()

type USB interface {
	Start()
	Stop()
}

type Phone struct {
	Brand string
}

type Carmer struct {
	Brand string
}

func (phone Phone) Start() {
	fmt.Println("phone start")
}

func (phone Phone) Stop() {
	fmt.Println("phone stop")
}

func (phone Phone) Call() {
	fmt.Println("phone Call")
}

func (carmer Carmer) Start() {
	fmt.Println("carmer start")
}

func (carmer Carmer) Stop() {
	fmt.Println("carmer stop")
}

type Computer struct {
}

// 多态参数
func (computer Computer) Connect(usb USB) {
	usb.Start()
	usb.Stop()
}

func main() {
	// 1. 多态参数的使用
	var phone Phone
	var carmer Carmer
	var computer Computer
	computer.Connect(phone)
	computer.Connect(carmer)
	// phone start
	// phone stop
	// carmer start
	// carmer stop
	// 2. 多态数组
	var usb [3]USB
	usb[0] = Phone{"xiaomi"}
	usb[1] = Carmer{"suoni"}
	usb[2] = Phone{"huawei"}
	fmt.Printf("usb: %v\n", usb) // usb: [{xiaomi} {suoni} {huawei}]

	for _, v := range usb {
		computer.Connect(v)
		p, err := v.(Phone)
		if err {
			p.Call()
		}
	}
	/*
		phone start
		phone stop
		phone Call
		carmer start
		carmer stop
		phone start
		phone stop
		phone Call
	*/
}

类型断言

type Point struct {
	x int
	y int
}

func main() {
	// 案例 1
	var a interface{}
	var point Point = Point{1, 2}
	a = point
	var b Point
	b = a.(Point)            // 类型断言
	fmt.Printf("b: %v\n", b) // b: {1 2}

	// 案例 2
	var c interface{}
	var d float32 = 5.6
	c = d
	e := c.(float32)
	fmt.Printf("e: %v\n", e) // e: 5.6

	// 案例 3 带判断的类型断言
	f, res := c.(int)
	if res { // res: false
		fmt.Printf("f: %v\n", f)
	} else {
		fmt.Printf("res: %v\n", res)
	}
}

示例

func TypeJudge(parameters ...interface{}) {
	for index, val := range parameters {
		switch val.(type) {
		case bool:
			fmt.Printf("index: %v  =bool= val: %v\n", index, val)
		case float32:
			fmt.Printf("index: %v  =float32= val: %v\n", index, val)
		case float64:
			fmt.Printf("index: %v  =float64= val: %v\n", index, val)
		case int, int32, int64, int8:
			fmt.Printf("index: %v  =ints= val: %v\n", index, val)
		case string:
			fmt.Printf("index: %v  =string= val: %v\n", index, val)
		case Student:
			fmt.Printf("index: %v  =Student= val: %v\n", index, val)
		case *Student:
			stu := val.(*Student)
			fmt.Printf("index: %v  =*Student= val: %v\n", index, *stu)
		default:
			fmt.Printf("index: %v  =no= val: %v\n", index, val)
		}
	}
}

type Student struct {
	Name string
}

func main() {
	n1 := 300
	var n2 float32 = 12.3
	var n3 float64 = 153.69
	name := "tom"
	address := "beijing"
	var nums []int = []int{1, 2, 3}
	var student Student = Student{"jack"}
	var stuPointer *Student = &Student{"mary"}
	TypeJudge(n1, n2, n3, name, address, nums, student, stuPointer)
	/*
		index: 0  =ints= val: 300
		index: 1  =float32= val: 12.3
		index: 2  =float64= val: 153.69
		index: 3  =string= val: tom
		index: 4  =string= val: beijing
		index: 5  =no= val: [1 2 3]
		index: 6  =Student= val: {jack}
		index: 7  =*Student= val: &{mary}
	*/
}

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

SoaringW

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值