go【第四篇】面向对象
概述
和传统面向对象的区别
对于面向对象编程的支持Go 语言设计得非常简洁而优雅。
因为, Go语言并没有沿袭传统面向对象编程中的诸多概念,比如继承(不支持继承,尽管匿名字段的内存布局和行为类似继承,但它并不是继承)、虚函数、构造函数和析构函数、隐藏的this指针等。
面向对象三大特性实现方式
尽管Go语言中没有封装、继承、多态这些概念,但同样通过别的方式实现这些特性:
封装:通过方法实现 继承:通过匿名字段实现 多态:通过接口实现
对象创建
package main import "fmt" type Person struct { id int name string age int } func main() { var person = Person{101, "张三", 18} var person2 = Person{102, "李四", 19} fmt.Println(stu, stu2) }
package main import "fmt" type Student struct { Person score float64 } type Person struct { id int name string age int } func main() { //未全部初始化所有字段需要显式指出字段 var stu = Student{Person{101, "张三", 18}, 90} var stu2 = Student{score:90} var stu3 = Student{Person:Person{101, "张三", 18}} var stu4 = Student{Person:Person{id:101, }} fmt.Println(stu) fmt.Println(stu2) fmt.Println(stu3) fmt.Println(stu4) } /*输出 {{101 张三 18} 90} {{0 0} 90} {{101 张三 18} 0} {{101 0} 0} */
成员操作
package main import "fmt" type Student struct { Person id int score float64 } type Person struct { id int name string age int } func main() { var stu = Student{Person{101, "张三", 18}, 90,88} var stu1 = Student{Person{102, "李四", 18}, 80, 99} fmt.Println(stu, stu1) stu.score = 91 fmt.Println(stu, stu1) fmt.Println("张三考试成绩:", stu.score) fmt.Println("李四考试成绩:", stu1.score) //如果子对象字段和父对象重名 fmt.Println("李四考试id:", stu1.Person.id) fmt.Println("李四考试id:", stu1.id) } /*输出 {{101 张三 18} 90 88} {{102 李四 18} 80 99} {{101 张三 18} 90 91} {{102 李四 18} 80 99} 张三考试成绩: 91 李四考试成绩: 99 李四考试id: 102 李四考试id: 80 */
指针类型匿名字段
package main import "fmt" type Student struct { *Person // 匿名字段 score float64 } type Person struct { id int name string age int } func main() { var stu Student=Student{&Person{101,"张三",18},90} fmt.Println(stu.name, stu.Person) } //输出 //张三 &{101 张三 18}
多重继承
package main import "fmt" type Student struct { Person score float64 } type Person struct { Object name string age int } type Object struct { id int } func main() { var stu Student stu.age = 18 fmt.Println(stu.Person.age) stu.id = 101 fmt.Println(stu.Person.Object.id) fmt.Println(stu.id) } /* 18 101 101 */
基本方法创建
package main import "fmt" type Int int // type 为int类型指定了别名. func (a Int) TestInt(b Int) Int { return a + b } func main() { var num Int = 10 var num1 Int = 20 n := num.TestInt(20) n1 := num1.TestInt(5) fmt.Println(n) fmt.Println(n1) }
为结构体添加方法
package main import "fmt" type Student struct { id int name string age int } // 接收者 func (s Student) PrintShow() { fmt.Println(s) } func (s *Student) EditInfo() { //以后一般采用传结构体的指针方式,因为可以影修改构体的内容 s.age = 20 } func main() { stu := Student{101, "张三", 18} stu.PrintShow() // 完成对方法的调用, 将stu中的值,传递给了方法的s. stu.EditInfo() stu.PrintShow() }
package main import "fmt" type Student struct { id int name string age int } type Teacher struct { id int name string } func(s *Student) Show(){ fmt.Println(s) } func(t *Teacher) Show(){ fmt.Println(t) } func main() { // 如果接收者类型不同,即使方法的名字是相同的也是不同的方法。 stu:=Student{101,"李四",18} //(&stu).Show() stu.Show() teacher:=Teacher{102,"老王"} teacher.Show() }
package main import "fmt" //定义一个学生类,有六个属性,分别为姓名、性别、年龄、语文、数学、英语成绩 //第一方法:打招呼的方法:介绍自己叫XX,今年几岁了。是男同学还是女同学。 //第二个方法:计算总分与平均分的方法 // 1: 定义结构体 type StudentInfo struct { name string sex string age int chinese float64 math float64 english float64 } // 2: 添加方法 func (s *StudentInfo) SayHello(userName string, userAge int, userSex string) { // 2.1 初始化 s.name = userName s.age = userAge s.sex = userSex // 2.2 初始化后的值进行判断。 if s.sex != "男" && s.sex != "女" { s.sex = "男" } if s.age < 1 || s.age > 100 { s.age = 18 } // 2.3 打印输出结果 fmt.Printf("我叫%s,年龄是%d,性别是%s\n", s.name, s.age, s.sex) } func (s *StudentInfo) GetScore(chinese float64, math float64, english float64) { // 1: 初始化 s.chinese = chinese s.math = math s.english = english // 2: 进行计算 sum := s.chinese + s.math + s.english // 3: 打印输出结果 fmt.Printf("我叫%s,总分%f,平均分%f", s.name, sum, sum/3) } func main() { var stu StudentInfo stu.SayHello("张三", 180, "df") stu.GetScore(90,89,87) }
封装
封装变量
结构体封装在变量中
封装函数
在Go语言中,可以给任意自定义类型(包括内置类型,但不包括指针类型)添加相应的方法。
语法: func (receiver ReceiverType) funcName(parameters) (results)
只要接收者类型不一样,这个方法就算同名,也是不同方法,不会出现重复定义函数的错误
package main import "fmt" type MyInt int //自定义类型,给int改名为MyInt //在函数定义时,在其名字之前放上一个变量,即是一个方法 func (a MyInt) Add(b MyInt) MyInt { //面向对象 return a + b } //传统方式的定义 func Add(a, b MyInt) MyInt { //面向过程 return a + b } func main() { var a MyInt = 1 var b MyInt = 1 //调用func (a MyInt) Add(b MyInt) fmt.Println("a.Add(b) = ", a.Add(b)) //a.Add(b) = 2 //调用func Add(a, b MyInt) fmt.Println("Add(a, b) = ", Add(a, b)) //Add(a, b) = 2 }
package main import "fmt" type Person struct { name string //名字 sex byte //性别, 字符类型 age int //年龄 } //带有接收者的函数叫方法 func (tmp Person) PrintInfo() { fmt.Println("tmp = ", tmp) } //通过一个函数,给成员赋值 func (p *Person) SetInfo(n string, s byte, a int) { p.name = n p.sex = s p.age = a } func main() { //定义同时初始化 p := Person{"mike", 'm', 18} p.PrintInfo() //定义一个结构体变量 var p2 Person (&p2).SetInfo("yoyo", 'f', 22) p2.PrintInfo() }
继承
继承变量
结构体中定义另一个结构体类型的变量
继承方法
如果匿名字段实现了一个方法,那么包含这个匿名字段的struct也能调用该方法。
package main import "fmt" type Person struct { name string //名字 sex byte //性别, 字符类型 age int //年龄 } //Person类型,实现了一个方法 func (tmp *Person) PrintInfo() { fmt.Printf("name=%s, sex=%c, age=%d\n", tmp.name, tmp.sex, tmp.age) } //有个学生,继承Person字段,成员和方法都继承了 type Student struct { Person //匿名字段 id int addr string } func main() { s := Student{Person{"mike", 'm', 18}, 666, "bj"} s.PrintInfo() }
重写
package main import "fmt" type Person struct { name string //名字 sex byte //性别, 字符类型 age int //年龄 } //Person类型,实现了一个方法 func (tmp *Person) PrintInfo() { fmt.Printf("name=%s, sex=%c, age=%d\n", tmp.name, tmp.sex, tmp.age) } //有个学生,继承Person字段,成员和方法都继承了 type Student struct { Person //匿名字段 id int addr string } //Student也实现了一个方法,这个方法和Person方法同名,这种方法叫重写 func (tmp *Student) PrintInfo() { fmt.Println("Student: tmp = ", tmp) } func main() { s := Student{Person{"mike", 'm', 18}, 666, "bj"} //就近原则:先找本作用域的方法,找不到再用继承的方法 s.PrintInfo() //到底调用的是Person, 还是Student, 结论是Student //显式调用继承的方法 s.Person.PrintInfo() }
方法值
package main import "fmt" type Person struct { name string //名字 sex byte //性别, 字符类型 age int //年龄 } func (p Person) SetInfoValue() { fmt.Printf("SetInfoValue: %p, %v\n", &p, p) } func (p *Person) SetInfoPointer() { fmt.Printf("SetInfoPointer: %p, %v\n", p, p) } func main() { p := Person{"mike", 'm', 18} fmt.Printf("main: %p, %v\n", &p, p) p.SetInfoPointer() //传统调用方式 //保存方式入口地址 pFunc := p.SetInfoPointer //这个就是方法值,调用函数时,无需再传递接收者,隐藏了接收者 pFunc() //等价于 p.SetInfoPointer() vFunc := p.SetInfoValue vFunc() //等价于 p.SetInfoValue() }
方法表达式
换个传参形式
package main import "fmt" type Person struct { name string //名字 sex byte //性别, 字符类型 age int //年龄 } func (p Person) SetInfoValue() { fmt.Printf("SetInfoValue: %p, %v\n", &p, p) } func (p *Person) SetInfoPointer() { fmt.Printf("SetInfoPointer: %p, %v\n", p, p) } func main() { p := Person{"mike", 'm', 18} fmt.Printf("main: %p, %v\n", &p, p) //方法值 f := p.SetInfoPointer //隐藏了接收者 //方法表达式 f := (*Person).SetInfoPointer f(&p) //显式把接收者传递过去 ====》 p.SetInfoPointer() f2 := (Person).SetInfoValue f2(p) //显式把接收者传递过去 ====》 p.SetInfoValue() }
接口
接口定义
type Humaner interface { SayHi() }
接⼝命名习惯以 er 结尾 接口只有方法声明,没有实现,没有数据字段 接口可以匿名嵌入其它接口,或嵌入到结构中
package main import "fmt" type Personer interface { SayHello() //Say() } type Student struct { } func (s *Student)SayHello(){ fmt.Println("老师好") } type Teacher struct { } func (t *Teacher)SayHello() { fmt.Println("学生好") } func main() { // 对象名.方法名 var stu Student //stu.SayHello() //var teacher Teacher //teacher.SayHello() // 通过接口变量来调用,必须都实现接口中所声明的方法。 var person Personer person=&stu person.SayHello() // 调用的是Student 实现的SayHello方法 //person=&teacher //person.SayHello() }
package main import "fmt" type Personer interface { SayHello() } type Student struct { } func (s *Student)SayHello(){ fmt.Println("老师好") } type Teacher struct { } func ( t *Teacher)SayHello() { fmt.Println("学生好") } // 多态。 func WhoSayHi(h Personer) { h.SayHello() } func main() { var stu Student var teacher Teacher WhoSayHi(&stu) WhoSayHi(&teacher) }
package main import "fmt" type Stroager interface { Read() Writer() } // 移动硬盘 type MDisk struct { } func (m *MDisk) Read() { fmt.Println("移动硬盘读取数据") } func (m *MDisk) Writer() { fmt.Println("移动硬盘写入数据") } // U盘 type UDisk struct { } func (u *UDisk) Read() { fmt.Println("U盘读取数据") } func (u *UDisk) Writer() { fmt.Println("U盘写入数据") } // 定义一个函数 func Computer(c Stroager){ c.Read() c.Writer() } func main() { var udisk UDisk var mdisk MDisk Computer(&udisk) Computer(&mdisk) }
接口实现
接口变量指向绑定好函数的结构体实例,指向后即可调用接口方法
package main import "fmt" //------------------ 定义接口类型 --------------------------------- type Humaner interface { //方法,只有声明,没有实现,由别的类型(自定义类型)实现 sayhi() } // ------------------ 封装Student Teacher --------------------------------- type Student struct { name string id int } //Student实现了此方法 func (tmp *Student) sayhi() { fmt.Printf("Student[%s, %d] sayhi\n", tmp.name, tmp.id) } type Teacher struct { addr string group string } //Teacher实现了此方法 func (tmp *Teacher) sayhi() { fmt.Printf("Teacher[%s, %s] sayhi\n", tmp.addr, tmp.group) } func main() { s := &Student{"mike", 666} t := &Teacher{"bj", "go"} var i, j Humaner i, j = s, t i.sayhi() j.sayhi() }
package main import "fmt" //------------------ 定义接口类型 --------------------------------- type Humaner interface { //方法,只有声明,没有实现,由别的类型(自定义类型)实现 sayhi() } // ------------------ 封装Student Teacher --------------------------------- type Student struct { name string id int } //Student实现了此方法 func (tmp *Student) sayhi() { fmt.Printf("Student[%s, %d] sayhi\n", tmp.name, tmp.id) } type Teacher struct { addr string group string } //Teacher实现了此方法 func (tmp *Teacher) sayhi() { fmt.Printf("Teacher[%s, %s] sayhi\n", tmp.addr, tmp.group) } // ------------------ 方便接口使用 --------------------------------- //只有一个函数,可以有不同表现,多态 func Fsayhi(i Humaner) { i.sayhi() } func main() { s := &Student{"mike", 666} t := &Teacher{"bj", "go"} //调用同一函数,不同表现,多态,多种形态 Fsayhi(s) Fsayhi(t) }
package main import "fmt" //------------------ 定义接口类型 --------------------------------- type Humaner interface { //方法,只有声明,没有实现,由别的类型(自定义类型)实现 sayhi() } // ------------------ 封装Student Teacher MyStr --------------------------------- type Student struct { name string id int } //Student实现了此方法 func (tmp *Student) sayhi() { fmt.Printf("Student[%s, %d] sayhi\n", tmp.name, tmp.id) } type Teacher struct { addr string group string } //Teacher实现了此方法 func (tmp *Teacher) sayhi() { fmt.Printf("Teacher[%s, %s] sayhi\n", tmp.addr, tmp.group) } type MyStr string //MyStr实现了此方法 func (tmp *MyStr) sayhi() { fmt.Printf("MyStr[%s] sayhi\n", *tmp) } // ------------------ 方便接口使用 --------------------------------- //只有一个函数,可以有不同表现,多态 func Fsayhi(i Humaner) { i.sayhi() } func main() { s := &Student{"mike", 666} t := &Teacher{"bj", "go"} var str MyStr = "hello mike" //调用同一函数,不同表现,多态,多种形态 Fsayhi(s) Fsayhi(t) Fsayhi(&str) }
接口继承
package main import "fmt" type Humaner interface { //子集 sayhi() } type Personer interface { //超集 Humaner //匿名字段,继承了sayhi() sing(lrc string) } type Student struct { name string id int } //Student实现了sayhi() func (tmp *Student) sayhi() { fmt.Printf("Student[%s, %d] sayhi\n", tmp.name, tmp.id) } //sing(lrc string) func (tmp *Student) sing(lrc string) { fmt.Println("Student在唱着:", lrc) } func main() { //定义一个接口类型的变量 var i Personer s := &Student{"mike", 666} i = s i.sayhi() //继承过来的方法 i.sing("学生哥") }
接口转换
package main import "fmt" type Humaner interface { SayHello() } type Personer interface { Humaner Say() } type Student struct { } func (s *Student) SayHello() { fmt.Println("大家好") } func (s *Student) Say() { fmt.Println("你好") } func main() { var stu Student var per Personer per = &stu //per.Say() //per.SayHello() // 可以调用所继承的接口中的方法。 var h Humaner h=per // per=h h.SayHello() }
package main import "fmt" type Humaner interface { //子集 sayhi() } type Personer interface { //超集 Humaner //匿名字段,继承了sayhi() sing(lrc string) } type Student struct { name string id int } //Student实现了sayhi() func (tmp *Student) sayhi() { fmt.Printf("Student[%s, %d] sayhi\n", tmp.name, tmp.id) } func (tmp *Student) sing(lrc string) { fmt.Println("Student在唱着:", lrc) } func main() { //超集可以转换为子集,反过来不可以 var iPro Personer //超集 iPro = &Student{"mike", 666} var i Humaner //子集 //iPro = i //err i = iPro //可以,超集可以转换为子集 i.sayhi() }
空接口
空接口(interface{})不包含任何的方法,正因为如此,所有的类型都实现了空接口,因此空接口可以存储任意类型的数值。它有点类似于C语言的void *类型。
var v1 interface{} = 1 // 将int类型赋值给interface{} var v2 interface{} = "abc" // 将string类型赋值给interface{} var v3 interface{} = &v2 // 将*interface{}类型赋值给interface{} var v4 interface{} = struct{ X int }{1} var v5 interface{} = &struct{ X int }{1}
断言
Go语言里面有一个语法,可以直接判断是否是该类型的变量: value, ok = element.(T),这里value就是变量的值,ok是一个bool类型,element是interface变量,T是断言的类型。 如果element里面确实存储了T类型的数值,那么ok返回true,否则返回false。
package main import "fmt" type Element interface{} type Student struct { name string id int } func main() { var i [3]Element //i := make([]Element, 3) i[0] = 1 //int i[1] = "hello go" //string i[2] = Student{"mike", 666} //Student //类型查询,类型断言 //第一个返回下标,第二个返回下标对应的值, data分别是i[0], i[1], i[2] for index, data := range i { //第一个返回的是值,第二个返回判断结果的真假 if value, ok := data.(int); ok == true { fmt.Printf("x[%d] 类型为int, 内容为%d\n", index, value) } else if value, ok := data.(string); ok == true { fmt.Printf("x[%d] 类型为string, 内容为%s\n", index, value) } else if value, ok := data.(Student); ok == true { fmt.Printf("x[%d] 类型为Student, 内容为name = %s, id = %d\n", index, value.name, value.id) } } }
断言与空接口
package main import "fmt" // 加法类 type Add struct { Object } func (a *Add) GetResult() int { // 方法的实现要和接口中方法的声明保持一致 return a.numA + a.numB } func (a *Add) SetData(data ...interface{}) bool { // 1: 对数据的个数进行校验。 var b bool = true if len(data) > 2 { fmt.Println("参数个数错误!!") b = false } value, ok := data[0].(int) if !ok { fmt.Println("第一个数类型错误") b = false } value1, ok1 := data[1].(int) if !ok1 { fmt.Println("第二个数据类型错误") b = false } a.numA = value a.numB = value1 // 2: 类型进行校验。 return b } // 减法类 type Sub struct { Object } func (s *Sub) SetData(data ...interface{}) bool { // 1: 对数据的个数进行校验。 var b bool = true if len(data) > 2 { fmt.Println("参数个数错误!!") b = false } value, ok := data[0].(int) if !ok { fmt.Println("第一个数类型错误") b = false } value1, ok1 := data[1].(int) if !ok1 { fmt.Println("第二个数据类型错误") b = false } s.numA = value s.numB = value1 // 2: 类型进行校验。 return b } func (s *Sub) GetResult() int { return s.numA - s.numB } type Object struct { numA int numB int } type Resulter interface { GetResult() int SetData(data ...interface{}) bool // 完成参数运算的数据的类型校验。 } // 对象问题 // 1: 定义一个新的类 type OperatorFactory struct { } // 2: 创建一个方法,在该方法中完成对象的创建 func (o *OperatorFactory) CreateOperator(op string) Resulter { switch op { case "+": add := new(Add) return add case "-": sub := new(Sub) return sub default: return nil } } func OperatorWho(h Resulter) int { return h.GetResult() } func main() { var operator OperatorFactory obj:=operator.CreateOperator("-") b:=obj.SetData(30,10) if b { num:=OperatorWho(obj) fmt.Println(num) } }
谢谢