1. GO语言OOP概述
classstructclassstructOOP继承重载构造函数析构函数隐藏this指针封装继承多态语言类型系统
封装继承多态privateprotectedpublic继承多继承duck typing
2. 封装的实现
构造函数私有属性的方法
示例
// 文件结构如下
|__model
|____person.go
|__main.go
person.go
package model
// 这是一`私有`person 类型结构体
// 包外不能直接生生成一个该类型的实例
// person 首字母小写包外不见
type person struct {
// 首字母大写保外可见
Name string
// 首字母小写保外不可见
age int8
Work string
deposit float64
hobbys []string
}
// 构造函数
// 包外可见
func NewPerson(name string, age int8, work string, deposit float64) *person {
return &person{
Name: name,
age: age,
Work: work,
deposit: deposit,
}
}
// 给person类型数据添加各种方法
func (p *person) GetAge() int8 {
return p.age
}
func (p person) GetDeposit() float64 {
return p.deposit
}
func (p *person) SetHobby(h string) {
p.hobbys = append(p.hobbys, h)
}
func (p person) GetHobbys() []string {
return p.hobbys
}
main.go
package main
import (
"GoNote/chapter6/demo18/model"
"fmt"
)
func main(){
// 定义一个结构体变量p1
p1 := model.NewPerson("tom",26,"UI",50000.01)
fmt.Println(p1)
// 只能访问可见属性, 成员属性age和deposit是访问不到的
fmt.Println(p1.Name,p1.Work)
// 通过可见方法访问不可见的属性
fmt.Println(p1.GetAge())
fmt.Println(p1.GetDeposit())
// 调用设置的方法
(*p1).SetHobby("跑步")
p1.SetHobby("爬山")
// 嗲用获取的方法
fmt.Println(p1.GetHobbys())
}
go run main.go
&{tom 26 UI 50000.01 []}
tom UI
26
50000.01
[跑步 爬山]
3. 继承的实现
匿名结构体组合
package main
import "fmt"
// 定义一个Good结构体
type Good struct {
name string
productDate string
price float64
}
// 定义一个Books结构体
type Books struct {
// 内嵌Good类型的结构体
Good
// Books自由的成员字段
classify string
publishing string
author string
}
// 定义Vendor结构体
type Vendor struct {
VendorName string
retailPrice float64
grade float32
}
// 定义ProgrammingBook 的结构体
type ProgrammingBook struct {
// 构成组合
// 此处类似于多继承
b Books
v Vendor
overview string
chapters []string
}
// 给Good类型绑定一个方法
func (g *Good) setGoodInfo(name string,pd string,price float64) {
g.name = name
g.productDate = pd
g.price = price
}
func (g *Good) GoodDescribe() string {
return fmt.Sprintf("商品名称是%s,价格是%.2f,生产日期是%s\n", g.name, g.price, g.productDate)
}
// 给Books绑定一个BasicInfo的方法
func (b *Books) booksDescribe() string {
return fmt.Sprintf("书的分类是%s,出版商是%s,作者是%s\n", b.classify, b.publishing, b.author)
}
func (p *ProgrammingBook) GetBook() {
//可以调用
ginfo := p.b.GoodDescribe()
binfo := p.b.booksDescribe()
fmt.Printf("商品信息 :%s 书籍信息 :%s 书籍概览 :%s",ginfo,binfo,p.overview)
}
func main() {
var gobook ProgrammingBook
// 结构体变量Gobook 内嵌了 Books类型结构体,Books结构体又和Good是组合的
//不是很严谨的说法:相当于ProgrammingBook 继承 Books ,Books继承了Good , 那么ProgrammingBook也继承Good的属性和方法
// 所以ProgrammingBook的实例 gobook能调用Good的方法
gobook.b.setGoodInfo("Go程序设计语言","2019-10-01",99.90)
// ProgrammingBook结构体内嵌了有名结构体Books,Vendor(组合),相当于继承了他们属性和方法
gobook.b.classify = "计算机|编程"
gobook.b.publishing = "机械工业出版社"
gobook.b.author = "艾伦 A.A.多诺万"
gobook.v.VendorName = "当当网"
gobook.v.retailPrice = 89.0
gobook.v.grade = 9.2
gobook.overview = "号称Go语言界的圣经"
gobook.GetBook()
}
go run main.go
商品信息 :商品名称是Go程序设计语言,价格是99.90,生产日期是2019-10-01
书籍信息 :书的分类是计算机|编程,出版商是机械工业出版社,作者是艾伦 A.A.多诺万
书籍概览 :号称Go语言界的圣经
就近原则多继承
package main
import "fmt"
type AA struct {
name string
AAAddr string
}
type BB struct {
name string
BBAddr string
}
type DD struct {
name string
DDAddr string
}
type XX struct {
AA
name string
XXAddr string
}
type CC struct {
AA
BB
XX
d DD
name string
}
// 给AA结构体类型添加方法demo1
func (a *AA) demo1(){
fmt.Println(a.name)
}
// 给DD结构体类型添加方法demo1
func (d *DD) demo1(){
fmt.Println(d.name)
}
// 给CC结构体类型添加方法demo1
func (c *CC) demo1(){
fmt.Println(c.name)
}
func main(){
ins1 := new(CC)
// 此时访问的是CC结构体自己字段name(就近原则)
ins1.name = "Name-CC"
// CC 结构体内嵌了匿名结构体AA,CC,他们的name在逻辑山处于同一层级
// 要访问AA或者BB中的name 那就必须带上结构类型名
ins1.AA.name = "Name-AA"
// 多层级内嵌匿名结构体,访问方式也是逐层范文过去
ins1.XX.AA.name = "XX(Name-AA)"
// DD是CC有名结构体内嵌,或者叫组合
ins1.d.name = "Name-DD"
// 匿名结构体并且成员字段是唯一的,可以这样直接访问
ins1.BBAddr = "addr of BB"
// 组合类型的 有名内嵌那就必须带上名称
ins1.d.DDAddr = "addr of DD"
// 调用的是自己的方法 CC 结构体绑定的方法
ins1.demo1()
// 调用组合DD的demo1方法
ins1.d.demo1()
// 调用内嵌的匿名结构体AA的demo1方法
ins1.AA.demo1()
ins1.XX.demo1()
}
go run main.go
Name-CC
Name-DD
Name-AA
XX(Name-AA)
4. 多态的实现
多态interface
4.1 接口概述
接口
4.2 声明接口
typeinterfaceer
package main
type 接口类型名 interface {
函数名1(参数列表1) 返回值列表1
函数名2(参数列表2) 返回值列表2
函数名3(参数列表3) 返回值列表3
函数名4(参数列表4) 返回值列表4
函数名5(参数列表5) 返回值列表5
}
func main(){
}
package main
type Demoer interface {
func1(int,string) (int ,string)
func2(int,int) (int ,error)
func3(s1 ,s2 string) (s3 string ,err error)
func4()
}
func main(){
}
4.3 实现接口
接口的实现由两个基本原则
- 接口的方法和实现接口的类型的方法格式必须完全一致
- 接口中的所有方法都被实现了才能说接口被实现了
package main
import "fmt"
type Doer interface {
Music()
ShowTv()
}
// 实现了接口Doer
type MP4 struct {
}
func (m MP4) Music() {
fmt.Println("play music")
}
func (m MP4) ShowTv() {
fmt.Println("play Tv")
}
func main() {
var m = new(MP4)
m.Music()
m.ShowTv()
}
一个类型可以实现多个接口
一个接口可以被多个类型实现
package main
import "fmt"
// Volume类型 实现CommonFunc接口
type Volume int
type BasicFunc interface {
Start()
Stop()
}
// 接口 CommonFunc 被两种数据类型实现
// 多种数据类型可以实现相同的接口
type CommonFunc interface {
VolumeIncrease()
VolumeReduce()
}
// TV类型结构体实现了CommonFunc和BasicFunc接口
type TV struct {
}
func (v Volume) VolumeIncrease() {
fmt.Println("音量增加")
}
func (v Volume) VolumeReduce() {
fmt.Println("音量减少")
}
func (t TV) Start() {
fmt.Println("开机")
}
func (t TV) Stop() {
fmt.Println("关机")
}
func (t TV) VolumeIncrease() {
fmt.Println("音量增加")
}
func (t TV) VolumeReduce() {
fmt.Println("音量减少")
}
func main() {
}
4.4 接口嵌套
Go语言中,接口与接口之间可以通过嵌套创造出新的接口
嵌套产生的新接口要被实现那么它嵌套关联的接口都被实现才行
简单讲 : X接口 是可以继承多个接口 如A接口 B接口 ,如果想实现X接口,就必须实现A,B接口中的所有方法
package main
// A 接口被Demo类型事项
type A interface {
FuncA()
}
// B 接口被Demo类型实现
type B interface {
FuncB()
}
// x 接口被Demo类型实现
type X interface {
A
B
}
type Demo struct {
}
func (a Demo) FuncA() {
}
func (b Demo) FuncB() {
}
func main() {
}
4.5 类型断言的格式
t := i.(T)
// t表示转换之后的变量
// i表示接口变量
// T表示转换的目标类型
接口类型可以接受任何的的数据,但是还是不知道到时什么类型,所以需要使用类型断言
package main
import "fmt"
func main(){
// 定义接口类型变量 i
var i interface{}
// 定义浮点类型变量f
var f float64 = 98.90
// 因为i是空接口类型所以可以接受如何变量
i = f
x := i.(float64)
fmt.Printf("x type = %T,value = %0.2f",x,x)
}
go run main.go
x type = float64,value = 98.90
t,ok := i.(T)
// 和上述的类型断言格式一样
// ok 表示类型匹配是否成功的的标识,ok的类型是bool, 值为true表示成功,false表示失败 ok这个变量名是惯例写法,变量名可以自定义
package main
import "fmt"
func main() {
// 定义接口类型变量 i
var i interface{}
// 定义浮点类型变量f
var f float64 = 98.90
// 因为i是空接口类型所以可以接受如何变量
i = f
x := i.(float64)
fmt.Printf("x type = %T,value = %0.2f\n", x, x)
y ,ok:= i.(float32)
fmt.Printf("ok type = %T,value = %v\n", ok, ok)
if ok{
fmt.Printf("x type = %T,value = %0.2f\n", y, y)
}else{
fmt.Println("类型不符合")
}
}
go run main.go
x type = float64,value = 98.90
ok type = bool,value = false
类型不符合
switch 任意类型变量.(type){
case 类型1:
处理逻辑1
case 类型2:
处理逻辑2
...
default:
处理逻辑n
}
package main
import "fmt"
type Demo1 struct {
data map[string]string
}
type Demo2 struct {
}
func PrintType (v interface{}) {
switch v.(type) {
case int :
fmt.Println(v,"is int")
case string :
fmt.Println(v,"is string")
case bool :
fmt.Println(v ,"is bool")
case Demo1:
fmt.Println(v,"is Demo1")
case Demo2 :
fmt.Println(v,"is Demo2")
}
}
func main(){
PrintType(99)
PrintType("golang")
PrintType(true)
demo1 := Demo1{data: map[string]string{"name":"tom"}}
PrintType(demo1)
demo2 := Demo2{}
PrintType(demo2)
}
go run main.go
99 is int
golang is string
true is bool
{map[name:tom]} is Demo1
{} is Demo2
4.6 空接口类型 interface{}
空接口是接口类型的特殊形式, 空接 口没有任何方法,因此任何类型都无须实现空接口,从实现的角度看,任何值都满足这个接口的需求
- 空接口也是一种类型
- 空间接口可以接受任何值
package main
import "fmt"
// 声明一个interface{}类型变量any
var any interface{}
func main() {
// 将int型 数据99 赋值给any
any = 99
fmt.Printf("any type = %T, value = %v\n",any,any)
// 声明一个int型变量i
var i int
// 那么是不是可以将any直接赋值给i呢?
// 实际是不行的,因为any本质上是interface{}类型,而不是int型
// i = any
// 执行之后编译器会抛出
// cannot use any (type interface {}) as type int in assignment: need type assertion
// 所以我们需要用到类型断言,将interface{}类型转换成int类型赋值给i
i = any.(int)
fmt.Printf("i type = %T, value = %v\n",i,i)
any = "golang"
fmt.Printf("any type = %T, value = %v\n",any,any)
any = true
fmt.Printf("any type = %T, value = %v\n",any,any)
}
看一个使用interface{} 的例子
package main
import "fmt"
type Dict struct {
// data 作为map类型可以接受任意类型的键和值
data map[interface{}]interface{}
}
// 增加数据
func (d *Dict) addValue(key, value interface{}) {
d.data[key] = value
}
// 获取数据
func (d *Dict) GetValue(i interface{}) interface{} {
return d.data[i]
}
// 清空
func (d *Dict) DelAll() {
d.data = make(map[interface{}]interface{})
}
func (d *Dict) Visit(callback func(k,v interface{}) bool ) {
if callback == nil{
return
}
for key,value := range d.data{
if ! callback(key,value){
return
}
}
}
// 初始化
func NewDict() *Dict {
d := new(Dict)
d.DelAll()
return d
}
func main() {
d := NewDict()
d.addValue("name", "tom")
d.addValue("male", true)
name := d.GetValue("name")
fmt.Println(name)
// 访问
d.Visit(func(k, v interface{}) bool {
fmt.Println(k,v)
return true
})
}
go run main.go
tom
name tom
male true
4.7 如何实现多态
接口变量
参数
package main
import (
"fmt"
)
// 定义一个接口USB,有两个方法
type USB interface {
start()
stop()
}
// 定义Phone的类型的结构体
type Phone struct {
name string
}
// Phoen类型实现接口USB
func (p Phone) start() {
fmt.Println(p.name, "start")
}
func (p Phone) stop() {
fmt.Println(p.name, "stop")
}
// 定义Pad类型的结构体
type Pad struct {
name string
}
// Pad实现接口USB
func (p Pad) start() {
fmt.Println(p.name, "start")
}
func (p Pad) stop() {
fmt.Println(p.name, "stop")
}
// 定义machine类型的结构体
type Machine struct{
}
// 给Machine绑定方法Work
// 传入的参数是USB类型,凡是实现了USB的变量都可以是参数
func (m Machine) Work(u USB){
u.start()
u.stop()
}
func main() {
var iphon Phone = Phone{"iphone"}
var ipad Pad = Pad{"ipad"}
// 实现接口的类型变量,可以赋值给接口变量
// 被赋值的接口变量,可以像类型变量一样调用接口方法
// 接口方法被具体实现的不一样,所有调用相同的方法,结果也是不一样的
var usb USB
// 将实例iPhone赋值给接口变量usb
usb = iphon
usb.start()
// 将实例iPad赋值给接口变量usb
usb = ipad
usb.start()
m := Machine{}
// iPhone Iphone 实现USB接口 所以iPhone可以是变量
// 因为USB接口的具体实现的不同,故同样的USB接口呈现了多态
m.Work(iphon)
m.Work(ipad)
}
go run main.go
iphone start
ipad start
iphone start
iphone stop
ipad start
ipad stop
实现多态方法1:
把接口当做数据类型,那么实现该接口的结构体(或者其他类型) 都是符合该接口类型
package main
import (
"fmt"
)
// 定义一个接口USB,有两个方法
type USB interface {
start()
stop()
}
// 定义Phone的类型的结构体
type Phone struct {
name string
}
// Phoen类型实现接口USB
func (p Phone) start() {
fmt.Println(p.name, "start")
}
func (p Phone) stop() {
fmt.Println(p.name, "stop")
}
// 定义Pad类型的结构体
type Pad struct {
name string
}
// Pad实现接口USB
func (p Pad) start() {
fmt.Println(p.name, "start")
}
func (p Pad) stop() {
fmt.Println(p.name, "stop")
}
func main() {
// 定义一个map变量iArr它的键是string类型,值是USB接口类型
// 那么只有实现了USB接口的数据才能符合
var iArr map[string]USB
iArr = make(map[string]USB)
// Phone结构体类型 实现了USB ,所以可以将Phone类型的结构体可以当做值赋给变量IArr
iArr["iPhone X"] = Phone{"iPhonex"}
// ipad结构体类型 实现了USB ,所以可以将ipad类型的结构体可以当做值赋给变量IArr
iArr["IPad2018"] = Pad{"Ipad2018款"}
// 调用方法
iArr["IPad2018"].start()
iArr["IPad2018"].stop()
iArr["P30"] = Phone{"华为P30"}
iArr["Mate20"] = Phone{"Mate 20 X"}
iArr["P30"].stop()
iArr["P30"].start()
}
go run main.go
Ipad2018款 start
Ipad2018款 stop
华为P30 stop
华为P30 start