面向对象编程思想-抽象
抽象的介绍
我们在前面去定义一个结构体时候,实际上就是把一类事物的共有的 属性( 字段)和 行为( 方法)提取
出来,形成一个 物理模型(结构体)。这种研究问题的方法称为抽象
比如一个银行账户:
package main import ( "fmt" ) //定义一个结构体Account type Account struct { AccountNo string Pwd string Balance float64 } //方法 //1. 存款 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 || money > account.Balance { 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 : "gs1111111", Pwd : "666666", Balance : 100.0, } //这里可以做的更加灵活,就是让用户通过控制台来输入命令... //菜单.... account.Query("666666") account.Deposite(200.0, "666666") account.Query("666666") account.WithDraw(150.0, "666666") account.Query("666666") }
面向对象编程三大特性-封装
基本介绍
Golang 仍然有面向对象编程的继承,封装和多态的特性,只是实现的方式和其它 OOP 语言不一
样,下面我们一一为同学们进行详细的讲解 Golang 的三大特性是如何实现的。
封装介绍
封装(encapsulation)就是把抽象出的字段和对字段的操作封装在一起,数据被保护在内部,程序的其
它包只有通过被授权的操作(方法),才能对字段进行操作
封装的理解和好处
1) 隐藏实现细节
2) 提高对 数据进行验证,保证安全合理(Age)
如何体现封装
1) 对结构体中的属性进行封装
2) 通过 方法,包 包 实现封装
封装的实现步骤
1) 将结构体、字段(属性)的首字母小写(不能导出了,其它包不能使用,类似 private)
2) 给结构体所在包提供一个工厂模式的函数,首字母大写。类似一个构造函数
3) 提供一个首字母大写的 Set 方法(类似其它语言的 public),用于对属性判断并赋值
func (var 结构体类型名) SetXxx(参数列表) (返回值列表) {
//加入数据验证的业务逻辑
var.字段 = 参数
}
4) 提供一个首字母大写的 Get 方法(类似其它语言的 public),用于获取属性的值
func (var 结构体类型名) GetXxx() {
return var.age;
}
特别说明:在 Golang 开发中并没有特别强调封装,这点并不像 Java. 所以提醒学过 java 的朋友,
不用总是用 java 的语法特性来看待 Golang, Golang 本身对面向对象的特性做了简化的.
看一个案例
请大家看一个程序(person.go),不能随便查看 人的年龄, 工资等隐私,并对输入的年龄进行合理的验
证。设计: model 包(person.go) main 包(main.go 调用 Person 结构体)
main.go
package main import ( "fmt" "go_code/code/chapter11/encapsulate/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()) }
moudel
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 <150 { p.age = age } else { fmt.Println("年龄范围不正确..") //给程序员给一个默认值 } } func (p *person) GetAge() int { return p.age } func (p *person) SetSal(sal float64) { if sal >= 3000 && sal <= 30000 { p.sal = sal } else { fmt.Println("薪水范围不正确..") } } func (p *person) GetSal() float64 { return p.sal }
面向对象编程三大特性-继承
看一个问题,引出继承的必要性
一个小问题,看个学生考试系统的程序 extends01.go,提出代码复用的问题
1) Pupil 和 Graduate 两个结构体的字段和方法几乎,但是我们却写了相同的代码, 代码复用性不
强
2) 出现代码冗余,而且代码 不利于维护,同时 也不利于功能的扩展。
3) 解决方法-通过 继承方式来解决
package main import ( "fmt" ) //编写一个学生考试系统 type Student struct { Name string Age int Score int } //将Pupil 和 Graduate 共有的方法也绑定到 *Student 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 } //给 *Student 增加一个方法,那么 Pupil 和 Graduate都可以使用该方法 func (stu *Student) GetSum(n1 int, n2 int) int { return n1 + n2 } //小学生 type Pupil struct { Student //嵌入了Student匿名结构体 } //显示他的成绩 //这时Pupil结构体特有的方法,保留 func (p *Pupil) testing() { fmt.Println("小学生正在考试中.....") } //大学生, 研究生。。 //大学生 type Graduate struct { Student //嵌入了Student匿名结构体 } //显示他的成绩 //这时Graduate结构体特有的方法,保留 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() fmt.Println("res=", pupil.Student.GetSum(1, 2)) graduate := &Graduate{} graduate.Student.Name = "mary~" graduate.Student.Age = 28 graduate.testing() graduate.Student.SetScore(90) graduate.Student.ShowInfo() fmt.Println("res=", graduate.Student.GetSum(10, 20)) }
继承可以解决代码复用,让我们的编程更加靠近人类思维。
当多个结构体存在相同的属性(字段)和方法时,可以从这些结构体中抽象出结构体(比如刚才的
Student),在该结构体中定义这些相同的属性和方法。
其它的结构体不需要重新定义这些属性(字段)和方法,只需嵌套一个 Student 匿名结构体即可
也就是说:在 Golang 中,如果一个 struct 嵌套了另一个匿名结构体,那么这个结构体可以直接访
问匿名结构体的字段和方法,从而实现了继承特性。
嵌套匿名结构体的基本语法
type Goods struct { Name string Price int } type Book struct { Goods //这里就是嵌套匿名结构体 Goods Writer string }
继承给编程带来的便利
1) 代码的复用性提高了
2) 代码的扩展性和维护性提高了
继承的深入讨论
1) 结构体可以 使用嵌套匿名结构体所有的字段和方法,即:首字母大写或者小写的字段、方法,
都可以使用。【举例说明】
2) 匿名结构体字段访问可以简化
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 } func (b *B) SayOk() { fmt.Println("B SayOk", b.Name) } func main() { // var b B // b.A.Name = "tom" // b.A.age = 19 // b.A.SayOk() // b.A.hello() // //上面的写法可以简化 // b.Name = "smith" // b.age = 20 // b.SayOk() // b.hello() var b B b.Name = "jack" // ok b.A.Name = "scott" b.age = 100 //ok b.SayOk() // B SayOk jack b.A.SayOk() // A SayOk scott b.hello() // A hello ? "jack" 还是 "scott" }
对上面的代码小结
(1) 当我们直接通过 b 访问字段或方法时,其执行流程如下比如 b.Name
(2) 编译器会先看 b 对应的类型有没有 Name, 如果有,则直接调用 B 类型的 Name 字段
(3) 如果没有就去看 B 中嵌入的匿名结构体 A 有没有声明 Name 字段,如果有就调用,如果没有
继续查找..如果都找不到就报错.
3) 当 结构体和 匿名结构体有相同的字段或者方法时, 编译器采用就近访问原则访问,如希望访问
匿名结构体的字段和方法,可以通过匿名结构体名来区分【举例说明】
4) 结构体嵌入两个(或多个)匿名结构体,如 两个匿名结构体有相同的字段和方法( 同时结构体本身
没有同名的字段和方法),在访问时,就必须明确指定匿名结构体名字,否则编译报错。【举例说明】
5) 如果一个 struct 嵌套了一个有名结构体,这种模式就是 组合,如果是组合关系,那么在访问组合
的结构体的字段或方法时,必须带上结构体的名字
6) 嵌套匿名结构体后,也可以在创建结构体变量(实例)时,直接指定各个 匿名结构体字段的值
说明
1) 如果一个结构体有 int 类型的匿名字段,就不能第二个。
2) 如果需要有多个 int 的字段,则必须给 int 字段指定名字
面向对象编程-多重继承
多重继承说明
如 一个 struct 嵌套了多个匿名结构体,那么该结构体可以直接访问嵌套的匿名结构体的字段和方
法, 从而实现了多重继承。
多重继承细节说明
1) 如嵌入的匿名结构体有相同的字段名或者方法名,则在访问时,需要通过匿名结构体类型名来
区分。【案例演示】
2) 为了保证代码的简洁性,建议大家尽量不使用多重继承
package main import ( "fmt" ) type A struct { Name string age int } type B struct { Name string Score float64 } type C struct { A B //Name string } type D struct { a A //有名结构体 } type Goods struct { Name string Price float64 } type Brand struct { Name string Address string } type TV struct { Goods Brand } type TV2 struct { *Goods *Brand } type Monster struct { Name string Age int } type E struct { Monster int n int } func main() { var c C //如果c 没有Name字段,而A 和 B有Name, 这时就必须通过指定匿名结构体名字来区分 //所以 c.Name 就会包编译错误, 这个规则对方法也是一样的! c.A.Name = "tom" // error fmt.Println("c") //如果D 中是一个有名结构体,则访问有名结构体的字段时,就必须带上有名结构体的名字 //比如 d.a.Name var d D d.a.Name = "jack" //嵌套匿名结构体后,也可以在创建结构体变量(实例)时,直接指定各个匿名结构体字段的值 tv := TV{ Goods{"电视机001", 5000.99}, Brand{"海尔", "山东"}, } //演示访问Goods的Name fmt.Println(tv.Goods.Name) fmt.Println(tv.Price) tv2 := TV{ Goods{ Price : 5000.99, Name : "电视机002", }, Brand{ Name : "夏普", Address :"北京", }, } fmt.Println("tv", tv) fmt.Println("tv2", tv2) tv3 := TV2{ &Goods{"电视机003", 7000.99}, &Brand{"创维", "河南"}, } tv4 := TV2{ &Goods{ Name : "电视机004", Price : 9000.99, }, &Brand{ Name : "长虹", Address : "四川", }, } fmt.Println("tv3", *tv3.Goods, *tv3.Brand) fmt.Println("tv4", *tv4.Goods, *tv4.Brand) //演示一下匿名字段时基本数据类型的使用 var e E e.Name = "狐狸精" e.Age = 300 e.int = 20 e.n = 40 fmt.Println("e=", e) }
接口(interface)
基本介绍
按顺序,我们应该学习多态,但是在学习多态前,我们需要讲解接口(interface),因为在 Golang 中 多态
特性主要是通过接口来体现的。
接口快速入门
这样的设计需求在 Golang 编程中也是会大量存在的,我曾经说过,一个程序就是一个世界,在现实世
界存在的情况,在程序中也会出现。 我们用程序来模拟一下前面的应用场景。
代码实现
package main import ( "fmt" ) //声明/定义一个接口 type Usb interface { //声明了两个没有实现的方法 Start() Stop() } //声明/定义一个接口 type Usb2 interface { //声明了两个没有实现的方法 Start() Stop() Test() } 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)要使用的时候,在根据具体情况把这些方法写出来(实现)。
说明
1) 接口里的 所有方法都没有方法体,即接口的方法都是没有实现的方法。接口体现了程序设计的
多态和 高内聚低偶合的思想。
2) Golang 中的接口, 不需要 显式的实现。只要一个变量,含有接口类型中的所有方法,那么这个
变量就实现这个接口。因此,Golang 中 没有 implement 这样的关键字
注意事项和细节
1) 接口本身 不能创建实例,但是 可以指向一个实现了该接口的自定义类型的变量(实例)
2) 接口中所有的方法都没有方法体,即都是没有实现的方法。
3) 在 Golang 中,一个自定义类型需要将某个接口的所有方法都实现,我们说这个自定义类型实现了该接口。
4) 一个自定义类型只有实现了某个接口,才能将该自定义类型的实例(变量)赋给接口类型
5) 只要是自定义数据类型,就可以实现接口,不仅仅是结构体类型。
6) 一个自定义类型可以实现多个接口
7) Golang 接口中不能有任何变量
8) 一个接口(比如 A 接口)可以继承多个别的接口(比如 B,C 接口),这时如果要实现 A 接口,也必须将 B,C 接口的方法也全部实现。
9) interface 类型默认是一个指针(引用类型),如果没有对 interface 初始化就使用,那么会输出 nil
10) 空接口 interface{} 没有任何方法, 所以所有类型都实现了空接 口, 即我们可以 把任何一个变量
赋给空接口。
package main import ( "fmt" ) type Stu struct { Name string } func (stu Stu) Say() { fmt.Println("Stu Say()") } type integer int func (i integer) Say() { fmt.Println("integer Say i =" ,i ) } 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 stu Stu //结构体变量,实现了 Say() 实现了 AInterface var a AInterface = stu a.Say() var i integer = 10 var b AInterface = i b.Say() // integer Say i = 10 //Monster实现了AInterface 和 BInterface var monster Monster var a2 AInterface = monster var b2 BInterface = monster a2.Say() b2.Hello() }
package main import ( "fmt" ) type BInterface interface { test01() } type CInterface interface { test02() } type AInterface interface { BInterface CInterface test03() } //如果需要实现AInterface,就需要将BInterface CInterface的方法都实现 type Stu struct { } func (stu Stu) test01() { } func (stu Stu) test02() { } func (stu Stu) test03() { } type T interface{ } func main() { var stu Stu var a AInterface = stu a.test01() var t T = stu //ok 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{} // 错误! 会报 Stu类型没有实现Usb接口 , // 如果希望通过编译, var u Usb = &stu var u Usb = stu u.Say() fmt.Println("here", u) }
接口编程的最佳实践
实现对 Hero 结构体切片的排序: sort.Sort(data Interface)
package main import ( "fmt" "sort" "math/rand" ) //1.声明Hero结构体 type Hero struct{ Name string Age int } //2.声明一个Hero结构体切片类型 type HeroSlice []Hero //3.实现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] } //1.声明Student结构体 type Student struct{ Name string Age int Score float64 } //将Student的切片,安Score从大到小排序!! func main() { //先定义一个数组/切片 var intSlice = []int{0, -1, 10, 7, 90} //要求对 intSlice切片进行排序 //1. 冒泡排序... //2. 也可以使用系统提供的方法 sort.Ints(intSlice) fmt.Println(intSlice) //请大家对结构体切片进行排序 //1. 冒泡排序... //2. 也可以使用系统提供的方法 //测试看看我们是否可以对结构体切片进行排序 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 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) // i=20 j = 10 }
实现接口 vs 继承
大家可能会对实现接口和继承比较迷茫了, 那么他们究竟有什么区别呢
package main import ( "fmt" ) //Monkey结构体 type Monkey struct { Name string } //声明接口 type BirdAble interface { Flying() } type FishAble interface { Swimming() } func (this *Monkey) climbing() { fmt.Println(this.Name, " 生来会爬树..") } //LittleMonkey结构体 type LittleMonkey struct { Monkey //继承 } //让LittleMonkey实现BirdAble func (this *LittleMonkey) Flying() { fmt.Println(this.Name, " 通过学习,会飞翔...") } //让LittleMonkey实现FishAble func (this *LittleMonkey) Swimming() { fmt.Println(this.Name, " 通过学习,会游泳..") } func main() { //创建一个LittleMonkey 实例 monkey := LittleMonkey{ Monkey { Name : "悟空", }, } monkey.climbing() monkey.Flying() monkey.Swimming() }
1) 当 A 结构体继承了 B 结构体,那么 A 结构就自动的继承了 B 结构体的字段和方法,并且可以直
接使用
2) 当 A 结构体需要扩展功能,同时不希望去破坏继承关系,则可以去实现某个接口即可,因此我们可以认为:实现接口是对继承机制的补充.实现接口可以看作是对 继承的一种补充
接口和继承解决的解决的问题不同
继承的价值主要在于:解决代码的 复用性和 可维护性。
接口的价值主要在于: 设计,设计好各种规范(方法),让其它自定义类型去实现这些方法。
接口比继承更加灵活 Person Student BirdAble LittleMonkey
接口比继承更加灵活,继承是满足 is - a 的关系,而接口只需满足 like - a 的关系。
接口在一定程度上实现 代码解耦
面向对象编程-多态
基本介绍
变量(实例)具有多种形态。面向对象的第三大特征,在 Go 语言,多态特征是通过接口实现的。可以按照统一的接口来调用不同的实现。这时接口变量就呈现不同的形态。
快速入门
在前面的 Usb 接口案例,Usb usb ,既可以接收手机变量,又可以接收相机变量,就体现了 Usb 接口 多态特性。
接口体现多态的两种形式
多态参数
在前面的 Usb 接口案例,Usb usb ,即可以接收手机变量,又可以接收相机变量,就体现了 Usb 接多态。
多态数组
演示一个案例:给 Usb 数组中,存放 Phone 结构体 和 Camera 结构体变量
案例说明:
package main import ( "fmt" ) //声明/定义一个接口 type Usb interface { //声明了两个没有实现的方法 Start() Stop() } type Phone struct { name string } //让Phone 实现 Usb接口的方法 func (p Phone) Start() { fmt.Println("手机开始工作。。。") } func (p Phone) Stop() { fmt.Println("手机停止工作。。。") } type Camera struct { name string } //让Camera 实现 Usb接口的方法 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 //oK // 如何将 a 赋给一个Point变量? var b Point // b = a 不可以 // b = a.(Point) // 可以 b = a.(Point) fmt.Println(b) // //类型断言的其它案例 // var x interface{} // var b2 float32 = 1.1 // x = b2 //空接口,可以接收任意类型 // // x=>float32 [使用类型断言] // y := x.(float32) // fmt.Printf("y 的类型是 %T 值是=%v", y, y) //类型断言(带检测的) var x interface{} var b2 float32 = 2.1 x = b2 //空接口,可以接收任意类型 // x=>float32 [使用类型断言] //类型断言(带检测的) if y, ok := x.(float32); ok { fmt.Println("convert success") fmt.Printf("y 的类型是 %T 值是=%v", y, y) } else { fmt.Println("convert fail") } fmt.Println("继续执行...") }
对上面代码的说明:
在进行类型断言时,如果类型不匹配,就会报 panic, 因此进行类型断言时,要确保原来的空接口
指向的就是断言的类型.
如何在进行断言时,带上检测机制,如果成功就 ok,否则也不要报 panic
类型断言的最佳实践 1
在前面的 Usb 接口案例做改进:
给 Phone 结构体增加一个特有的方法 call(), 当 Usb 接口接收的是 Phone 变量时,还需要调用 call
方法, 走代码:
package main import ( "fmt" ) //声明/定义一个接口 type Usb interface { //声明了两个没有实现的方法 Start() Stop() } type Phone struct { name string } //让Phone 实现 Usb接口的方法 func (p Phone) Start() { fmt.Println("手机开始工作。。。") } func (p Phone) Stop() { fmt.Println("手机停止工作。。。") } func (p Phone) Call() { fmt.Println("手机 在打电话..") } type Camera struct { name string } //让Camera 实现 Usb接口的方法 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() { //定义一个Usb接口数组,可以存放Phone和Camera的结构体变量 //这里就体现出多态数组 var usbArr [3]Usb usbArr[0] = Phone{"vivo"} usbArr[1] = Phone{"小米"} usbArr[2] = Camera{"尼康"} //遍历usbArr //Phone还有一个特有的方法call(),请遍历Usb数组,如果是Phone变量, //除了调用Usb 接口声明的方法外,还需要调用Phone 特有方法 call. =》类型断言 var computer Computer for _, v := range usbArr{ computer.Working(v) fmt.Println() } //fmt.Println(usbArr) }
类型断言的最佳实践 2
写一函数,循环判断传入参数的类型:
package main import ( "fmt" ) //定义Student类型 type Student struct { } //编写一个函数,可以判断输入的参数是什么类型 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 stu1 := Student{} stu2 := &Student{} TypeJudge(n1, n2, n3, name, address, n4, stu1, stu2) }
go的面向对象终于完了(^▽^)!~~~~