go 面向对象编程-2

Diane ·
更新时间:2024-11-10
· 607 次阅读

面向对象编程思想-抽象

定义一个结构体的时候,实际上就是把一类事物的共有的属性(字段)和行为(方法)提取出来,形成一个物理模型(结构体). 这种研究问题的方法称为抽象

在这里插入图片描述

package main import ( "fmt" ) type Account struct { AccountNo string Pwd string Balance float64 } //存款 func (account *Account) Deposite(money float64, pwd string) { if pwd != account.Pwd { fmt.Println("输入密码错误") return } if money <= 0 { fmt.Println("输入金额错误") return } account.Balance += money fmt.Println("存款成功") } //取款 func (account *Account) WithDraw(money float64, pwd string) { if pwd != account.Pwd { fmt.Println("输入密码错误") return } if money <= 0 { fmt.Println("输入金额错误") return } account.Balance -= money fmt.Println("取款成功") } //查询 func (account *Account) Query(pwd string) { if pwd != account.Pwd { fmt.Println("输入密码错误") return } fmt.Printf("你的账号为%v 余额=%v \n", account.AccountNo, account.Balance) } func main() { account := Account{ AccountNo: "zhangsan", Pwd: "111111", Balance: 100.00, } account.Query("111111") account.Deposite(1000, "111111") account.Query("111111") account.WithDraw(500, "111111") account.Query("111111") } 面向对象编程三大特性-封装

golang仍然有面向对象编程的继承,封装和多态的特性,只是实现的方式和其它OOP语言不一样

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

封装的理解和好处 隐藏实现细节 可以对数据进行验证,保证安全合理 如何体现封装 对结构体中的属性进行封装 通过方法,包 实现封装 封装的实现步骤 将结构体、字段(属性)的首字母小写(不能导出了,其它包不能使用,类似private) 给结构体所在包提供一个工厂模式的函数,首字母大写。类似一个构造函数 提供一个首字母大写的Set方法(类似其它语言的public),用于对属性判断并赋值 func(var 结构体类型名) SetXxx(参数列表) (返回值类型列表) { var.字段 = 参数 } 提供一个首字母大写的Get方法(类似其它语言的public), 用于获取属性的值 func(var 结构体类型名) GetXxx() { return var.age }

特别说明:在golang开发中并没有特别强调封装,这点并不像java,golang本身对面向对象的特性做了简化

案例
person.go 不能随便查看人的年龄,工资等隐私,并对输入的年龄进行合理的验证。设计: model包 (person.go) main包(main.go 调用 person结构体)

model/person.go

package model import "fmt" type person struct { Name string age int sal float64 } //写一个工厂模式的函数, 相当于构造函数 func NewPerson(name string) *person { return &person{ Name: name, } } //为了访问 age 和 sal 编写一个对SetXxx的方法和 GetXxx的方法 func (p *person) SetAge(age int) { if age > 0 && age = 3000 && sal <= 30000 { p.sal = sal } else { fmt.Println("薪水范围错误...") } } func (p *person) GetSal() float64 { return p.sal }

main/main.go

package main import ( "fmt" "go_code/project01/model" ) func main() { p := model.NewPerson("smith") p.SetAge(18) p.SetSal(5000) fmt.Println(p) fmt.Println(p.Name, "age=", p.GetAge(), "sal=", p.GetSal()) }

创建程序,在model包中定义account结构体
account结构体要求具有字段: 账号(长度在6-10位之间)、余额(必须>20)、密码(必须是6位)
通过SetXxx的方法给account的字段赋值
main函数中测试

model/account.go

package model import "fmt" type account struct { accountNo string pwd string balance float64 } func NewAccount(accountNo string, pwd string, balance float64) *account { if len(accountNo) 10 { fmt.Println("账号长度不对...") return nil } if len(pwd) != 6 { fmt.Println("密码长度不对...") return nil } if balance <= 20 { fmt.Println("余额不对...") return nil } return &account{ accountNo: accountNo, pwd: pwd, balance: balance, } } //存款 func (account *account) Deposit(money float64, pwd string) { if pwd != account.pwd { fmt.Println("密码输入错误...") return } if money < 0 { fmt.Println("金额输入错误...") return } account.balance += money fmt.Println("存款成功") } //取款 func (account *account) WithDraw(money float64, pwd string) { if pwd != account.pwd { fmt.Println("密码输入错误") return } if money < 0 { fmt.Println("金额输入错误...") return } account.balance -= money fmt.Println("取款成功") } //查询 func (account *account) Query(pwd string) { if pwd != account.pwd { fmt.Println("密码输入错误") return } fmt.Printf("你的账号为%v 余额为%v \n", account.accountNo, account.balance) } func (account *account) GetAccountNo() string { return account.accountNo } func (account *account) SetAccountNo(accountNo string) { account.accountNo = accountNo }

main/main.go

package main import ( "fmt" "go_code/project01/model" ) func main() { account := model.NewAccount("zhangsan", "111111", 50) if account != nil { fmt.Println("创建成功 ", account) account.Query("111111") account.Deposit(1000, "111111") account.Query("111111") account.WithDraw(500, "111111") account.Query("111111") accountNo := account.GetAccountNo() fmt.Println("账户名 ", accountNo) account.SetAccountNo("zhangsan1") accountNo = account.GetAccountNo() fmt.Println("账户名改为 ", accountNo) } else { fmt.Println("创建失败") } } 面向对象编程三大特性-继承

考试管理系统

package main import "fmt" //小学生 type Pupil struct { Name string Age int Score int } func (p *Pupil) ShowInfo() { fmt.Printf("姓名=%v 年龄=%v 成绩=%v\n", p.Name, p.Age, p.Score) } func (p *Pupil) SetScore(score int) { p.Score = score } func (p *Pupil) testing() { fmt.Println("小学生正在考试中...") } //大学生 type Graduate struct { Name string Age int Score int } func (p *Graduate) ShowInfo() { fmt.Printf("姓名=%v 年龄=%v 成绩=%v\n", p.Name, p.Age, p.Score) } func (p *Graduate) SetScore(score int) { p.Score = score } func (p *Graduate) testing() { fmt.Println("大学生正在考试中...") } func main() { var pupil = &Pupil{ Name: "tom", Age: 10, } pupil.testing() pupil.SetScore(90) pupil.ShowInfo() var graduate = &Graduate{ Name: "mary", Age: 20, } graduate.testing() graduate.SetScore(80) graduate.ShowInfo() } Pupil 和 Graduate 两个结构体的字段和方法几乎一样,却写了相同的代码,代码复用性不强 出现代码冗余,而且代码不利于维护,同时也不利于功能的扩展 通过继承来解决 继承基本介绍

继承可以解决代码复用,让编程更加靠近人类思维

当多个结构体存在相同的属性(字段)和方法时,可以从这些结构体中抽象出结构体(比如 Student),在该结构体中定义这些相同的属性和方法

其它的结构体不需要重新定义这些属性(字段)和方法,只需嵌套一个Student匿名结构体即可

在这里插入图片描述

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

嵌套匿名结构体的基本语法 type Goods struct { Name string Price int } type Book struct { Goods //这里就是嵌套匿名结构体 Goods Writer string }

考试管理系统(使用继承)

package main import "fmt" type Student struct { Name string Age int Score int } func (stu *Student) ShowInfo() { fmt.Printf("姓名=%v 年龄=%v 成绩=%v\n", stu.Name, stu.Age, stu.Score) } func (stu *Student) SetScore(score int) { stu.Score = score } //小学生 type Pupil struct { Student //嵌入了Student匿名结构体 } func (p *Pupil) testing() { fmt.Println("小学生正在考试中...") } //大学生 type Graduate struct { Student } func (p *Graduate) testing() { fmt.Println("大学生正在考试中...") } func main() { pupil := &Pupil{} pupil.Student.Name = "tom" pupil.Student.Age = 8 pupil.testing() pupil.Student.SetScore(70) pupil.Student.ShowInfo() graduate := &Graduate{} graduate.Student.Name = "mary" graduate.Student.Age = 28 graduate.testing() graduate.Student.SetScore(90) graduate.Student.ShowInfo() } 继承给编程带来的便利 代码的复用性提高了 代码的扩展性和维护性提高了 继承的深入 结构体可以使用嵌套匿名结构体所有的字段和方法,即: 首字母大写或者小写的字段、方法,都可以使用 package main import "fmt" type A struct { Name string age int } func (a *A) SayOk() { fmt.Println("A SayOk", a.Name) } func (a *A) hello() { fmt.Println("A hello,", a.Name) } type B struct { A } func main() { var b B b.A.Name = "tom" b.A.age = 19 b.A.SayOk() b.A.hello() } 匿名结构体字段访问可以简化 package main import "fmt" type A struct { Name string age int } func (a *A) SayOk() { fmt.Println("A SayOk", a.Name) } func (a *A) hello() { fmt.Println("A hello,", a.Name) } type B struct { A } func main() { var b B /* b.A.Name = "tom" b.A.age = 19 b.A.SayOk() b.A.hello() */ b.Name = "tom" b.age = 19 b.SayOk() b.hello() }

说明:
直接通过b访问字段或方法时,其执行流程 比如 b.Name
编译器会先看b对应的类型有没有Name,如果有,则直接调用B类型的Name字段
如果没有就去看B中嵌入的匿名结构体A 有没有声明Name字段,如果有就调用,如果没有继续查找 … 如果都找不到就报错

当结构体和匿名结构体有相同的字段或方法时,编译器采用就近访问原则访问,如希望访问匿名结构体的字段和方法,可以通过匿名结构体名来区分 package main import "fmt" type A struct { Name string age int } func (a *A) SayOk() { fmt.Println("A SayOk", a.Name) } func (a *A) hello() { fmt.Println("A hello,", a.Name) } type B struct { A Name string age int } func (b *B) SayOk() { fmt.Println("B SayOk,", b.Name) } func (b *B) hello() { fmt.Println("B hello,", b.Name) } func main() { var b B b.Name = "tom" //这时就近原则,会访问B结构体的name字段 //b.A.Name 就明确指定访问A匿名结构体的字段Name b.A.Name = "jack" b.age = 78 b.SayOk() //这时就近原则,会访问B结构体的SayOk函数 b.hello() //b.A.hello()就明确指定访问A匿名结构体的方法hello() b.A.hello() }

运行结果:

B SayOk, tom B hello, tom A hello, jack 结构体嵌入两个(或多个)匿名结构体,如两个匿名结构体有相同的字段和方法(同时结构体本身没有同名的字段和方法),在访问时,就必须明确指定匿名结构体名字,否则编译报错 package main import "fmt" type A struct { Name string age int } type B struct { Name string Score float64 } type C struct { A B } func main() { var c C c.A.Name = "tom" fmt.Println(c) } 如果一个struct嵌套了一个有名结构体,这种模式就是组合, 如果是组合关系,那么在访问组合的结构体的字段或方法时,必须带上结构体的名字 package main import "fmt" type A struct { Name string age int } type B struct { Name string Score float64 } type C struct { A B } type D struct { a A //有名结构体 } func main() { var d D d.a.Name = "jack" fmt.Println(d) } 嵌套匿名结构体后,也可以在创建结构体变量(实例)时,直接指定各个匿名结构体字段的值 package main import "fmt" type Goods struct { Name string Price float64 } type Brand struct { Name string Address string } type TV struct { Goods Brand } type TV2 struct { *Goods *Brand } func main() { tv := TV{ Goods: Goods{"电视机01", 5000.99}, Brand: Brand{"海尔", "山东"}, } tv2 := TV{ Goods: Goods{ "电视机02", 5000.99, }, Brand: Brand{ "夏普", "北京", }, } fmt.Println("tv", tv) fmt.Println("tv2", tv2) tv3 := TV2{&Goods{"电视机03", 7000.99}, &Brand{"创维", "河南"},} tv4 := TV2{ &Goods{ "电视机04", 9000.99, }, &Brand{ "长虹", "四川", }, } fmt.Println("tv3", *tv3.Goods, *tv3.Brand) fmt.Println("tv4", *tv4.Goods, *tv4.Brand) } 结构体的匿名字段是基本数据类型,如何访问 package main import "fmt" type Monster struct { Name string Age int } type E struct { Monster int n int } func main() { var e E e.Name = "狐狸精" e.Age = 300 e.int = 20 e.n = 40 fmt.Println("e=", e) }

运行结果:

e= {{狐狸精 300} 20 40}

说明:

如果一个结构体有int类型的匿名字段,就不能有第二个 如果需要有多个int的字段,则必须给int字段指定名字 面向对象编程-多重继承

如一个struct嵌套了多个匿名结构体,那么该结构体可以直接访问嵌套的匿名结构体的字段和方法,从而实现了多重继承

package main import "fmt" type Goods struct { Name string Price float64 } type Brand struct { Name string Address string } type TV struct { Goods Brand } func main() { var tv TV tv.Goods.Name = "电视" tv.Price = 2000.99 fmt.Println(tv.Goods.Name) fmt.Println(tv.Price) }

说明:

如嵌入的匿名结构体有相同的字段名或方法名,则在访问时,需要通过匿名结构体类型名来区分 为了保证代码的简洁性,建议尽量不使用多重继承 接口(interface)

golang中多态特性主要是通过接口来体现的

package main import "fmt" //声明/定义一个接口 type Usb interface { //声明两个没有实现的方法 Start() Stop() } type Phone struct { } //让Phone实现 Usb接口的方法 func (p Phone) Start() { fmt.Println("手机开始工作...") } func (p Phone) Stop() { fmt.Println("手机停止工作...") } type Camera struct { } //让Camera实现 Usb接口的方法 func (c Camera) Start() { fmt.Println("相机开始工作...") } func (c Camera) Stop() { fmt.Println("相机停止工作...") } type Computer struct { } //编写一个方法Working方法,接收一个Usb接口类型变量 //只要是实现了Usb接口(所谓实现Usb接口,就是指实现了Usb接口声明所有方法) func (c Computer) Working(usb Usb) { //通过usb接口变量来调用Start和Stop方法 usb.Start() usb.Stop() } func main() { computer := Computer{} phone := Phone{} camera := Camera{} computer.Working(phone) computer.Working(camera) } 接口概念

interface类型可以定义一组方法,但是这些不需要实现。并且interface不能包含任何变量。到某个自定义类型(比如结构体Phone)要使用的时候,再根据具体情况把这些方法写出来(实现)

基本语法 type 接口名 interface { method1(参数列表) 返回值类型列表 method2(参数列表) 返回值类型列表 ... } //实现接口所有方法 func (t 自定义类型) method1(参数列表) 返回值类型列表 { //实现方法 } func (t 自定义类型) method2(参数列表) 返回值类型列表 { //实现方法 } ...

说明:

接口里的所有方法都没有方法体,即接口的方法都是没有实现的方法。接口体现了程序设计的多态高内聚低耦合的思想 golang中的接口,不需要显式的实现。只要一个变量,含有接口类型中的所有方法,那么这个变量就实现这个接口。

一个项目经理,管理三个程序员,开发一个软件,为了控制和管理软件,项目经理可以定义一些接口,然后由程序员具体实现

在这里插入图片描述

注意事项 接口本身不能创建实例,但是可以指向一个实现了该接口的自定义类型的变量(实例) package main import "fmt" type AInterface interface { Say() } type Stu struct { Name string } func (stu Stu) Say() { fmt.Println("Stu Say()") } func main() { var stu Stu //结构体变量,实现了Say() 实现了 AInterface var a AInterface = stu a.Say() } 接口中所有的方法都没有方法体,即都是没有实现的方法 在golang中,一个自定义类型需要将某个接口的所有方法都实现,就说这个自定义类型实现了该接口 一个自定义类型只有实现了某个接口,才能将该自定义类型的实例(变量)赋给接口类型 只要是自定义数据类型,就可以实现接口,不仅仅是结构体类型 package main import "fmt" type AInterface interface { Say() } type integer int func (i integer) Say() { fmt.Println("integer Say i =", i) } func main() { var i integer = 10 var b AInterface = i b.Say() } 一个自定义类型可以实现多个接口 package main import "fmt" type AInterface interface { Say() } type BInterface interface { Hello() } type Monster struct { } func (m Monster) Hello() { fmt.Println("Monster Hello()...") } func (m Monster) Say() { fmt.Println("Monster Say()...") } func main() { var monster Monster var a2 AInterface = monster var b2 BInterface = monster a2.Say() b2.Hello() } golang接口中不能有任何变量 一个接口(比如A接口)可以继承多个别的接口(比如B,C接口),这时如果要实现A接口,也必须将B,C接口的方法也全部实现 package main import "fmt" type BInterface interface { test01() } type CInterface interface { test02() } type AInterface interface { BInterface CInterface test03() } type Stu struct { } func (stu Stu) test01() { fmt.Println("test01()...") } func (stu Stu) test02() { fmt.Println("test02()...") } func (stu Stu) test03() { fmt.Println("test03()...") } func main() { var stu Stu var a AInterface = stu a.test01() } interface类型默认是一个指针(引用类型),如果没有对interface初始化就使用,那么会输出nil 空接口interface{} 没有任何方法,所以所有类型都实现了空接口,即可以把任何一个变量赋给空接口 package main import "fmt" type T interface { } type Stu struct { } func main() { var stu Stu var t T = stu fmt.Println(t) var t2 interface{} = stu var num1 float64 = 8.8 t2 = num1 t = num1 fmt.Println(t2, t) } package main import "fmt" type Usb interface { Say() } type Stu struct { } func (this *Stu) Say() { fmt.Println("Say()...") } func main() { var stu Stu = Stu{} var u Usb = &stu //注意这里 u.Say() fmt.Println("here", u) } 接口编程实战 import "sort"

func Sort

func Sort(data Interface)

Sort排序data。它调用1次data.Len确定长度,调用O(n*log(n))次data.Less和data.Swap。本函数不能保证排序的稳定性(即不保证相等元素的相对次序不变)

type Interface

type Interface interface { // Len方法返回集合中的元素个数 Len() int // Less方法报告索引i的元素是否比索引j的元素小 Less(i, j int) bool // Swap方法交换索引i和j的两个元素 Swap(i, j int) }

一个满足sort.Interface接口的(集合)类型可以被本包的函数进行排序。方法要求集合中的元素可以被整数索引。

实现对Hero结构体切片的排序: sort.Sort(data Interface)

package main import ( "fmt" "math/rand" "sort" ) type Hero struct { Name string Age int } //声明一个Hero结构体切片类型 type HeroSlice []Hero //实现Interface接口 func (hs HeroSlice) Len() int { return len(hs) } //Less方法就是决定你使用什么标准进行排序 //1. 按Hero的年龄从小到大排序 func (hs HeroSlice) Less(i, j int) bool { return hs[i].Age < hs[j].Age //修改成对Name排序 //return hs[i].Name < hs[j].Name } func (hs HeroSlice) Swap(i, j int) { //temp := hs[i] //hs[i] = hs[j] //hs[j] = temp //等价下面一句 hs[i], hs[j] = hs[j], hs[i] } //声明Student结构体 type Student struct { Name string Age int Score float64 } //将Student的切片,按Score从大到小排序 func main() { var intSlice = []int{0, -1, 10, 7, 90} sort.Ints(intSlice) fmt.Println(intSlice) var heroes HeroSlice for i := 0; i < 10; i++ { hero := Hero{ Name: fmt.Sprintf("英雄|%d", rand.Intn(100)), Age: rand.Intn(100), } //将hero append到heroes切片 heroes = append(heroes, hero) } for _, v := range heroes { fmt.Println(v) } sort.Sort(heroes) fmt.Println("------排序后------") for _, v := range heroes { fmt.Println(v) } i := 10 j := 20 i, j = j, i fmt.Println("i=", i, "j=", j) }

声明Student结构体

type Student struct { Name string Age int Score float64 }

将Student的切片,按Score从大到小排序

package main import ( "fmt" "math/rand" "sort" ) //声明Student结构体 type Student struct { Name string Age int Score float64 } type StudentSlice []Student func (stu StudentSlice) Len() int { return len(stu) } func (stu StudentSlice) Less(i, j int) bool { return stu[i].Score < stu[j].Score } func (stu StudentSlice) Swap(i, j int) { stu[i], stu[j] = stu[j], stu[i] } //将Student的切片,按Score从大到小排序 func main() { var studentes StudentSlice for i := 0; i < 10; i++ { student := Student{ Name: fmt.Sprintf("stu-%d", rand.Intn(100)), Age: 10 + rand.Intn(20), Score: float64(rand.Intn(100)), } studentes = append(studentes, student) } for _, v := range studentes { fmt.Println(v) } sort.Sort(sort.Reverse(studentes)) fmt.Println("------排序后------") for _, v := range studentes { fmt.Println(v) } } 实现接口 vs 继承 package main import "fmt" type Monkey struct { Name string } type BirdAble interface { Flying() } type FishAble interface { Swimming() } func (this *Monkey) climbing() { fmt.Println(this.Name, "生来会爬树") } type LittleMonkey struct { Monkey } func (this *LittleMonkey) Flying() { fmt.Println(this.Name, "通过学习,会飞...") } func (this *LittleMonkey) Swimming() { fmt.Println(this.Name, "通过学习,会游泳...") } func main() { monkey := LittleMonkey{ Monkey{ "悟空", }, } monkey.climbing() monkey.Flying() monkey.Swimming() }

说明:

当A结构体继承了B结构体,那么A结构体就自动的继承了B结构体的字段和方法,并且可以直接使用 当A结构体需要扩展功能,同时不希望去破坏继承关系,则可以去实现某个接口即可 实现接口可以看作是对 继承的一种补充

在这里插入图片描述

接口和继承解决的问题不同

继承的价值主要在于: 解决代码的复用性和可维护性 接口的价值在于: 设计,设计好各种规范(方法),让其它自定义类型去实现这些方法

接口比继承更加灵活 继承是满足 is - a的关系,而接口只需要满足 like - a 的关系

接口在一定程度上实现代码解耦

面向对象编程-多态

变量(实例)具有多种形态。面向对象的第三大特征,在go语言中,多态特征是通过接口实现的。可以按照统一的接口来调用不同的实现。这时接口变量就呈现不同的形态

Usb接口,Usb usb , 既可以接收手机变量,有可以接收相机变量,就体现了 Usb接口 多态特性

package main import "fmt" //声明/定义一个接口 type Usb interface { //声明两个没有实现的方法 Start() Stop() } type Phone struct { } //让Phone实现 Usb接口的方法 func (p Phone) Start() { fmt.Println("手机开始工作...") } func (p Phone) Stop() { fmt.Println("手机停止工作...") } type Camera struct { } //让Camera实现 Usb接口的方法 func (c Camera) Start() { fmt.Println("相机开始工作...") } func (c Camera) Stop() { fmt.Println("相机停止工作...") } type Computer struct { } //编写一个方法Working方法,接收一个Usb接口类型变量 //只要是实现了Usb接口(所谓实现Usb接口,就是指实现了Usb接口声明所有方法) func (c Computer) Working(usb Usb) { //usb变量会根据传入的实参,来判断到底是Phone还是Camera (usb接口变量就体现出多态的特点) //通过usb接口变量来调用Start和Stop方法 usb.Start() usb.Stop() } func main() { computer := Computer{} phone := Phone{} camera := Camera{} computer.Working(phone) computer.Working(camera) } 接口体现多态的两种形式 多态参数
Usb usb, 既可以接收手机变量,又可以接收相机变量,就体现了Usb接口多态 多态数组
给Usb数组中,存放Phone结构体和Camera结构体变量 package main import "fmt" type Usb interface { Start() Stop() } type Phone struct { name string } func (p Phone) Start() { fmt.Println("手机开始工作...") } func (p Phone) Stop() { fmt.Println("手机停止工作...") } type Camera struct { name string } func (c Camera) Start() { fmt.Println("相机开始工作...") } func (c Camera) Stop() { fmt.Println("相机停止工作...") } func main() { //定义一个Usb接口数组,可以存放Phone和Camera的结构体变量 //这里就体现出多态数组 var usbArr [3]Usb usbArr[0] = Phone{"vivo"} usbArr[1] = Phone{"小米"} usbArr[2] = Camera{"尼康"} fmt.Println(usbArr) } 类型断言 package main import "fmt" type Point struct { x int y int } func main() { var a interface{} var point Point = Point{1, 2} a = point var b Point b = a.(Point) //类型断言 fmt.Println(b) }

b = a.(Point) 就是类型断言,表示判断a是否指向Point类型的变量

基本介绍

类型断言,由于接口是一般类型,不知道具体类型,如果要转成具体类型,就需要使用类型断言

package main import "fmt" func main() { var x interface{} var b2 float64 = 1.1 x = b2 //空接口,可以接收任意类型 y := x.(float64) fmt.Printf("y 的类型是 %T 值是 =%v", y, y) }

说明:
在进行类型断言时,如果类型不匹配,就会包panic,因此进行类型断言时,要确保原来的空接口指向的就是断言的类型

如何在进行断言时,带上检测机制,如果成功就ok,否则也不要报panic

package main import "fmt" func main() { var x interface{} var b2 float64 = 2.1 x = b2 //类型断言(带检测) if y, ok := x.(float64); ok { fmt.Println("convert success") fmt.Printf("y 的类型是 %T 值是 = %v", y, y) } else { fmt.Println("convert fail") } fmt.Println("继续执行...") }

Usb接口案例改进
给Phone结构体增加一个特有的方法call(),当Usb接口接收的是Phone变量时,还需要调用call方法

package main import "fmt" type Usb interface { Start() Stop() } type Phone struct { name string } func (p Phone) Start() { fmt.Println("手机开始工作...") } func (p Phone) Stop() { fmt.Println("手机停止工作...") } func (p Phone) Call() { fmt.Println("手机在打电话...") } type Camera struct { name string } func (c Camera) Start() { fmt.Println("相机开始工作...") } func (c Camera) Stop() { fmt.Println("相机停止工作...") } type Computer struct { } func (computer Computer) Working(usb Usb) { usb.Start() //如果usb是指向Phone结构体变量,则需要调用Call方法 if phone, ok := usb.(Phone); ok { phone.Call() } usb.Stop() } func main() { var usbArr [3]Usb usbArr[0] = Phone{"vivo"} usbArr[1] = Phone{"小米"} usbArr[2] = Camera{"尼康"} var computer Computer for _, v := range usbArr { computer.Working(v) fmt.Println() } fmt.Println(usbArr) }

写一个函数,循环判断传入参数类型

package main import "fmt" type Student struct { Name string Age int } func TypeJudge(items... interface{}) { for index, x := range items { switch x.(type) { case bool: fmt.Printf("第%v个参数是 bool 类型, 值是%v\n", index, x) case float32: fmt.Printf("第%v个参数是 float32 类型, 值是%v\n", index, x) case float64: fmt.Printf("第%v个参数是 float64 类型, 值是%v\n", index, x) case int, int32, int64: fmt.Printf("第%v个参数是 整数 类型, 值是%v\n", index, x) case string: fmt.Printf("第%v个参数是 string 类型, 值是%v\n", index, x) case Student: fmt.Printf("第%v个参数是 Student 类型, 值是%v\n", index, x) case *Student: fmt.Printf("第%v个参数是 *Student 类型, 值是%v\n", index, x) default: fmt.Printf("第%v个参数是 不确定 类型, 值是%v\n", index, x) } } } func main() { var n1 float32 = 1.1 var n2 float64 = 2.3 var n3 int32 = 30 var name string = "tom" address := "北京" n4 := 300 var stu1 Student var stu2 *Student TypeJudge(n1, n2, n3, name, address, n4, stu1, stu2) }
作者:wuxingge



面向对象编程 GO 对象 面向对象

需要 登录 后方可回复, 如果你还没有账号请 注册新账号