go 语言面向对象 下
vscode 常用快捷键

面向对象的三大特性
介绍
Golang仍然有面向对象编程的继承、封装和多态的特性,只是实现的方式和其他OOP语言不一样
面向对象编程思想抽象
在前面定义结构体时候,实际上就是把一类事物的共有的属性和行为提取出来,形成一个物理模版,这种研究问题的方法称为抽象
package main
import (
"fmt"
)
//定义一个结构体
type Account struct {
AccountNo string
pwd string
Balance float64
}
//方法
func (a *Account) Deposite(money float64,pwd string){
//
if pwd != a.pwd {
fmt.Println("密码错误")
return
}
if money<=0 {
fmt.Println("存款金额不正确")
return
}
a.Balance+=money
fmt.Printf("存款成功,余额是%v\n",a.Balance)
}
func (a *Account) WithDrw(money float64,pwd string){
if pwd != a.pwd{
fmt.Println("密码错误")
return
}
if money > a.Balance|| money<=0{
fmt.Printf("余额不足,无法取%v\n",money)
return
}
a.Balance-=money
fmt.Printf("成功取钱%v元,账户余额%v\n",money,a.Balance)
}
func (a *Account) Query(pwd string){
if pwd == a.pwd{
fmt.Printf("账户%v,余额=%v\n",a.AccountNo,a.Balance)
}else{
fmt.Println("密码有误")
}
}
func main(){
t := &Account{"456765123","666666",67000}
t.Deposite(200,"666666")
t.Query("666666")
}
// PS D:\golang\goproject\src\src01\go_code\src> go run chapter10\deom01\mian.go
// 存款成功,余额是67200
// PS D:\golang\goproject\src\src01\go_code\src> go run chapter10\deom01\mian.go
// 存款成功,余额是67200密码有误
// PS D:\golang\goproject\src\src01\go_code\src> go run chapter10\deom01\mian.go
// 存款成功,余额是67200
// 密码有误
// PS D:\golang\goproject\src\src01\go_code\src> go run chapter10\deom01\mian.go
// 存款成功,余额是67200
// 账户456765123,余额=67200
// PS D:\golang\goproject\src\src01\go_code\src> go run chapter10\deom01\mian.go
// 存款成功,余额是67200
// 账户456765123,余额=67200
示例
package main
import (
"fmt"
)
//定义一个结构体
type Account struct {
AccountNo string
pwd string
Balance float64
}
//方法
func (a *Account) Deposite(money float64,pwd string){
//
if pwd != a.pwd {
fmt.Println("密码错误")
return
}
if money<=0 {
fmt.Println("存款金额不正确")
return
}
a.Balance+=money
fmt.Printf("存款成功,余额是%v\n",a.Balance)
}
func (a *Account) WithDrw(money float64,pwd string){
if pwd != a.pwd{
fmt.Println("密码错误")
return
}
if money > a.Balance|| money<=0{
fmt.Printf("余额不足,无法取%v\n",money)
return
}
a.Balance-=money
fmt.Printf("成功取钱%v元,账户余额%v\n",money,a.Balance)
}
func (a *Account) Query(pwd string){
if pwd == a.pwd{
fmt.Printf("账户%v,余额=%v\n",a.AccountNo,a.Balance)
}else{
fmt.Println("密码有误")
}
}
func main(){
t := &Account{"456765123","666666",67000}
for {
var R string
var c float64
fmt.Println("请选择操作的选项")
fmt.Println("1 查账户")
fmt.Println("2 存款")
fmt.Println("3 取款")
fmt.Println("4 退出")
fmt.Scanln(&R)
if R == "4" {
break
}
switch R{
case "1":
t.Query("666666")
case "2":
fmt.Println("输入存款金额")
fmt.Scanln(&c)
t.Deposite(c,"666666")
case "3":
fmt.Println("输入取款金额")
fmt.Scanln(&c)
t.WithDrw(c,"666666")
// case "4":
// break
default:
fmt.Println("输入不正确重新输入")
}
}
}
// PS D:\golang\goproject\src\src01\go_code\src> go run chapter10\deom01\mian.go
// 请选择操作的选项
// 1 查账户
// 2 存款
// 3 取款
// 4 退出
// 3
// 输入取款金额
// 10000000
// 余额不足,无法取1e+07
// 请选择操作的选项
// 1 查账户
// 2 存款
// 3 取款
// 4 退出
// 3
// 输入取款金额
// 100000
// 余额不足,无法取100000
// 请选择操作的选项
// 1 查账户
// 2 存款
// 3 取款
// 4 退出
// 4
进阶
package main
import (
"fmt"
)
//定义一个结构体
type Account struct {
AccountNo string
pwd string
Balance float64
}
//方法
func (a *Account) Deposite(money float64,pwd string){
//
if pwd != a.pwd {
fmt.Println("密码错误")
return
}
if money<=0 {
fmt.Println("存款金额不正确")
return
}
a.Balance+=money
fmt.Printf("存款成功,余额是%v\n",a.Balance)
}
func (a *Account) WithDrw(money float64,pwd string){
if pwd != a.pwd{
fmt.Println("密码错误")
return
}
if money > a.Balance|| money<=0{
fmt.Printf("余额不足,无法取%v\n",money)
return
}
a.Balance-=money
fmt.Printf("成功取钱%v元,账户余额%v\n",money,a.Balance)
}
func (a *Account) Query(pwd string){
if pwd == a.pwd{
fmt.Printf("账户%v,余额=%v\n",a.AccountNo,a.Balance)
}else{
fmt.Println("密码有误")
}
}
func main(){
t := &Account{"456765123","666666",67000}
look:
for {
var R string
var c float64
fmt.Println("请选择操作的选项")
fmt.Println("1 查账户")
fmt.Println("2 存款")
fmt.Println("3 取款")
fmt.Println("4 退出")
fmt.Scanln(&R)
switch R{
case "1":
t.Query("666666")
case "2":
fmt.Println("输入存款金额")
fmt.Scanln(&c)
t.Deposite(c,"666666")
case "3":
fmt.Println("输入取款金额")
fmt.Scanln(&c)
t.WithDrw(c,"666666")
case "4":
break look
default:
fmt.Println("输入不正确重新输入")
}
}
}
// 执行结果
// PS D:\golang\goproject\src\src01\go_code\src> go run chapter10\deom01\mian.go
// 请选择操作的选项
// 1 查账户
// 2 存款
// 3 取款
// 4 退出
// 4
面向对象编程封装
封装介绍
封装就是把抽象的字段和对字段的操作封装在一起,数据被保护在内部,程序的其他包只有通过被授权的操作方法,才能对字段进行操作
封装的理解
1)隐藏实现细节
2)可以对数据进行验证,保证安全合理
如何体现封装
1)对结构体中的属性进行封装
2)通过方法,包实现封装
封装实现的步骤
1)将结构体、字段(属性)的首字母小写(不能导出了,其它包不能使用,类似private)
2)给结构体所在包提供一个工厂模式函数,首字母大写,类似于一个构造器函数
3)提供一个首字母大写的(结构体名字)Set方法(参数列表)(返回值列表){
var.字段= 参数
}
4)提供一个首字母大写的Get方法{类似public},用于获取属性的值
func (结构体类型名字)Get方法()返回值列表{
return var.字段
}
特别说明在Golang开发中并没有特别强调封装,这点并不像Java。不要用Java的语法特性来看Golang,Golang本身面向对象特性做了简化
封装快速入门
示例
package main
import (
//"fmt"
//"fmt"
"src01/go_code/src/chapter10/deom02/model"
)
func main(){
s:=model.NewPerson("晨曦")
s.SetAge(56)
s.SetSal(25000)
s.Get()
s.GetAge()
s.GetSal()
}
//执行结果
// PS D:\golang\goproject\src\src01\go_code\src> go run chapter10\deom02\main\main.go
// 名字是晨曦,年龄是56,工资是25000
// 晨曦 的年龄是 56
// 晨曦 的工资是 25000
---------------------------------------------------------------------------------------------
package model
import "fmt"
type person struct {
Name string
age int
sal float64
}
func NewPerson(n string) *person { //构造函数
return &person{
Name: n,
}
}
func (d *person)SetAge(n int){
if n >0 && n<150{
d.age=n
}else{
fmt.Println("输入年龄不正确")
}
}
func (d *person)SetSal(n float64){
if n>=3000 && n<30000{
d.sal=n
}else {
fmt.Println("输入不正确")
}
}
func (d *person)GetAge(){
fmt.Println(d.Name,"的年龄是",d.age)
}
func (d *person)GetSal(){
fmt.Println(d.Name,"的工资是",d.sal)
}
func (d *person) Get() {
fmt.Printf("名字是%v,年龄是%v,工资是%v\n",d.Name,d.age,d.sal)
}
补充图

示例2
package main
import (
"fmt"
"src01/go_code/src/chapter10/deom03/model"
)
func main(){
t := model.ChuShi("123456",89,"123456")
// t.SetZH("678907")
// t.SetYe(2000)
// t.SetMa("1239897")
if t != nil{
fmt.Printf("账户是%v,余额是%v,密码是%v\n",t.GetZh(),t.GetYe(),t.GetMa())
}else {
fmt.Println("失败")
}
}
// 执行结果
// PS D:\golang\goproject\src\src01\go_code\src> go run chapter10\deom03\main\main.go
// 账户是123456,余额是89,密码是123456
--------------------------------------------------------------------------------------
package model
import "fmt"
type account struct {
zh string
ye float64
ma string
}
func ChuShi(z string,y float64,m string ) *account {
if len(z) < 6 || len(z) > 10 {
return nil
}
if y < 20{
return nil
}
if len(m) !=6 {
return nil
}
return &account{
zh: z,
ye: y,
ma: m,
}
}
// func ChuShi() *account{
// return &account{}
// }
func (A *account)SetZH(n string){
if len(n)>=6&&len(n)<=10{
A.zh=n
} else {
fmt.Println("账户长度不对")
}
}
func (A *account)SetYe(n float64){
if n>20{
A.ye=n
}else{
fmt.Println("余额不正确")
}
}
func (A *account)SetMa(n string){
if len(n)==6{
A.ma=n
}else{
fmt.Println("密码位数不正确")
}
}
func (A *account)GetZh() string{
return A.zh
}
func (A *account)GetYe() float64{
return A.ye
}
func (A *account)GetMa() string{
return A.ma
}
面向对象继承
代码出现冗余
package main
import (
"fmt"
)
//小学生
type Pupil struct{
Name string
Age int
Score float64
}
func (p *Pupil)ShowInfo(){
fmt.Printf("%v的年龄是%v,成绩是%v\n",p.Name,p.Age,p.Score)
}
func (p *Pupil)SetScoer(a float64) {
if a >=1||a <=100{
p.Score=a
}else {
fmt.Println("输入的分数不正确")
}
}
func (p *Pupil)Tesing(){
fmt.Printf("小学生%v正在考试\n",p.Name)
}
type Graduate struct{
Name string
Age int
Score float64
}
func (p *Graduate)ShowInfo(){
fmt.Printf("%v的年龄是%v,成绩是%v\n",p.Name,p.Age,p.Score)
}
func (p *Graduate)SetScoer(a float64) {
if a >=1||a <=100{
p.Score=a
}else {
fmt.Println("输入的分数不正确")
}
}
func (p Graduate)Tesing(){
fmt.Printf("大学生%v正在考试\n",p.Name)
}
func main(){
t := &Pupil{
Name: "小希",
Age: 12,
Score: 78.6,
}
t.Tesing()
t.ShowInfo()
c :=&Graduate{
Name: "炘南",
Age: 20,
Score: 89,
}
c.ShowInfo()
c.Tesing()
}
//代码冗余
//执行结果
// PS D:\golang\goproject\src\src01\go_code\src> go run chapter10\deom04\mian.go
// 小学生小希正在考试
// 小希的年龄是12,成绩是78.6
// 炘南的年龄是20,成绩是89
// 大学生炘南正在考试
继承方式实现
package main
import (
"fmt"
)
type xs struct{
Name string
Age int
Score float64
}
func (s *xs)SetScoer(a float64){
if a >0 && a<=100{
s.Score=a
}
}
func (s *xs)ShowInfo(){
fmt.Printf("%v学生的成绩%v,年龄%v\n",s.Name,s.Score,s.Age)
}
//
type Pupil struct{
xs //匿名结构体
}
func (p *Pupil)Tesing(){
fmt.Printf("小学生%v正在考试\n",p.Name)
}
type Graduate struct{
xs
}
func (p *Graduate)Tesing(){
fmt.Printf("大学生%v正在考试\n",p.Name)
}
func main(){
t :=&Pupil{xs{Name: "晨曦",Age:12,Score: 97 }}
t.Tesing()
t.ShowInfo()
t.Tesing()
c:=&Graduate{xs{Name: "炘南",Age: 23,Score: 98}}
c.ShowInfo()
c.Tesing()
}
//代码冗余
//执行结果
// PS D:\golang\goproject\src\src01\go_code\src> go run chapter10\deom05\mian.go
// 小学生晨曦正在考试
// 晨曦学生的成绩97,年龄12
// 小学生晨曦正在考试
// 炘南学生的成绩98,年龄23
// 大学生炘南正在考试
继承的优点
代码复用性提高
代码扩展性和维护性提高
继承的深入讨论
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)hollo(){
fmt.Println("A hello",a.Name)
}
type B struct{
A
}
func main(){
t:=B{A{Name: "你好",age: 78}}
t.A.hollo() //正规写法
t.hollo() //简写
t.Sayok()
}
//执行结果
// PS D:\golang\goproject\src\src01\go_code\src> go run chapter10\deom05\mian.go
// A hello 你好
// A hello 你好
// A Sayok 你好
(1)当直接通过通过t访问字段或方法时,其执行流程如下t.hollo
(2)编译器会先看b对应的类型有没有name ,如果有直接调用B这个类型的Name
(3)如果没有就去看B中嵌入的匿名结构体A里面有没有定义Name,有就直接调用A结构体力的Name,如果都找不到就报错了
3)当结构体和匿名结构体有相同的字段是,编译器采用就近访问原则访问,如果希望访问匿名结构的字段和方法,可以通过匿名结构体名来区分
package main
import (
"fmt"
)
type A struct{
Name string
age int
}
func (a *A)Sayok(){
fmt.Println("A Sayok",a.Name)
}
func (a *A)hollo(){
fmt.Println("A hello",a.Name)
}
type B struct{
A
Name string
}
func (b *B)Sayok(){
fmt.Println("B Sayok",b.Name)
}
func (b *B)hollo(){
fmt.Println("B hello",b.Name)
}
func main(){
t:=B{A{Name: "白小航",age: 78},"小杜"}
t.A.hollo() //正规写法
t.hollo() //此时这个方法是调用B结构的方法;默认遵守就近原则
t.A.hollo() //这个是调用A结构hollo的方法
t.Sayok() //此时这个是调用B结构体方法;默认遵守就近原则
t.A.hollo()//此时这个是调用A结构体方法
}
//执行结果
// PS D:\golang\goproject\src\src01\go_code\src> go run chapter10\deom05\mian.go
// A hello 白小航
// B hello 小杜
// A hello 白小航
// B Sayok 小杜
// A hello 白小航
4)结构体嵌入两个或多个匿名结构体,如两个匿名结构体有相同的字段和方法(同时,结构体本身没有同名的字段和方法),在访问时,就必须明确指定结构体名字,否则编译报错
package main
import (
"fmt"
)
type A struct{
Name string
age int
}
type B struct{
A
Name string
}
type C struct{
A
B
}
func main(){
var R C
R.A.Name="小光"
R.B.Name="小度"
fmt.Println("A=",R.A.Name)
fmt.Println("B=",R.B.Name)
}
//执行结果
// PS D:\golang\goproject\src\src01\go_code\src> go run chapter10\deom05\mian.go
// A= 小光
// B= 小度
5)如果一个struct嵌套了一个有名的结构体,这种模式就是组合,如果是组合关系,那么在访问组合结构体的字段或方法时,必须带上结构体名字
package main
import (
"fmt"
)
type A struct{
Name string
age int
}
type F struct{
a A
}
func main(){
var T F
T.a.Name="小曦" //如果结构体嵌套一个有名的结构体,这种模式是组合,那么调用方法或字段时必须带上结构体名字
T.a.age=19
fmt.Printf("%v的年龄是%v\n",T.a.Name,T.a.age)
}
// 执行结果
// PS D:\golang\goproject\src\src01\go_code\src> go run chapter10\deom05\mian.go
// 小曦的年龄是19
6)嵌套匿名结构体后,也可以在创建结构体实例的时候,直接指定各个匿名结构体字段的值
package main
import (
"fmt"
)
type A struct{
Name string
age int
}
type B struct{
A
Name string
}
type F struct{
a A
}
func main(){
T:=B{A{"小鹿",18},"小🐺"}
fmt.Println(T)
}
// 执行结果
// PS D:\golang\goproject\src\src01\go_code\src> go run chapter10\deom05\mian.go
// {{小鹿 18} 小🐺}
示例2
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(){
tv1:=TV{Goods{"电视剧001",5000},Brand{"海尔","山东"}}
tv2:=TV{
Goods{
Price: 5000.99,
Name: "电视机2",
},
Brand{
Name: "小米",
Address: "北京",
},
}
fmt.Println(tv1)
fmt.Println(tv2)
tv3:=TV2{&Goods{"电视剧003",5000},&Brand{"东芝","山东"}}
fmt.Println("tv3",*tv3.Goods,*tv3.Brand)
}
// 执行结果
// PS D:\golang\goproject\src\src01\go_code\src> go run chapter10\deom05\mian.go
// {{电视剧001 5000} {海尔 山东}}
// {{电视机2 5000.99} {小米 北京}}
// tv3 {电视剧003 5000} {东芝 山东}
多重继承的深入
package main
import (
"fmt"
)
type A struct{
Name string
age int
}
type B struct{
A
int //将内置类型直接作为匿名字段
}
func main(){
y :=B{}
fmt.Println(y.int)
y.int=8//修改匿名内置类型字段默认值
y.age=9
y.Name="tom"
fmt.Println(y)
}
// 执行结果
// PS D:\golang\goproject\src\src01\go_code\src> go run chapter10\deom05\mian.go
// 0
// {{tom 9} 8}
一个结构体嵌套了多个匿名结构体,那么该结构体可以直接访问嵌套的匿名结构体字段和方法,从而实现多重继承;尽量不要使用多重继承,容易混乱
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(){
tv1:=TV{Goods{"电视剧001",5000},Brand{"海尔","山东"}}
tv2:=TV{
Goods{
Price: 5000.99,
Name: "电视机2",
},
Brand{
Name: "小米",
Address: "北京",
},
}
fmt.Println(tv1)
fmt.Println(tv2)
tv3:=TV2{&Goods{"电视剧003",5000},&Brand{"东芝","山东"}}
fmt.Println("tv3",*tv3.Goods,*tv3.Brand)
}
// 执行结果
// PS D:\golang\goproject\src\src01\go_code\src> go run chapter10\deom05\mian.go
// {{电视剧001 5000} {海尔 山东}}
// {{电视机2 5000.99} {小米 北京}}
// tv3 {电视剧003 5000} {东芝 山东}
接口介绍interface
接口的设计需求在Golang编程需求中也会有大量存在,一个程序就是一个世界,在现实世界存在的情况,在程序中也会出现,用程序来模拟一下应用场景
入门
package main
import(
"fmt"
)
//定义接口
type Usb interface{
//声明两个没有实现的方法
Start()
Stop()
}
type Phone struct{
}
func (p Phone)Start(){
fmt.Println("手机开始工作")
}
func (p Phone)Stop(){
fmt.Println("手机停止工作")
}
type Camer struct{
}
func (p Camer)Start(){
fmt.Println("相机开始工作")
}
func (p Camer)Stop(){
fmt.Println("相机停止工作")
}
type Compose struct{
}
//此方法接收一个Usb 接口类型变量;只要实现了Usb方法;就是指实现了USB接口声明的所有方法
func (p Compose)Working(usb Usb){
usb.Start()
usb.Stop()
}
func main(){
compose:= Compose{}
phone:=Phone{}
//camer:=Camer{}
//关键点
compose.Working(phone)
//compose.Working(camer)
}
// 执行结果
// PS D:\golang\goproject\src\src01\go_code\src> go run chapter11\damo01\main.go
// 手机开始工作
// 手机停止工作
基本介绍
interface 类型可以定义一组方法,但这些方法不需要实现。并且interface 不能包含任何变量,到某个自定义类型(比如结构体)要使用是,根据具体情况把这些方法写出来;go语言里设计最精妙的是interface,它让面向对象,内容组织的实现非常方便;简单的说interface是一组method(方法)的组合,通过interface来定义对象一组行为
基本语法语法
type 接口类型名 interface{
方法1(参数列表1) 返回值列表1
方法2(参数列表2) 返回值列表2
...
}
1) 接口里的所有方法没有方法体,即接口的方法都是没有实现的方法,接口体现了程序设计的多态和高内聚低耦合性的思想
2) Golang中的接口,不需要显式的实现。只有一个变量,含有接口类型中的所有方法,那么这个变量就可以实现这个接口,因此Glang中没有implement 这样的关键字
应用场景


接口注意事项
1)接口本身不能创建实例,但是可以指向一个实现了该接口的自定义类型的变量(实例)
package main
import(
"fmt"
)
//定义接口这个接口的含义是:任何类型,只要实现了 Say() 方法,就属于 Ainterface
type Ainterface interface{
Say()
}
//定义结构体这一步非常关键:👉 Stu 已经“实现了”接口 Ainterface
type Stu struct{
Name string
}
//这一步非常关键:👉 Stu 已经“实现了”接口 Ainterface
func (stu Stu) Say(){
fmt.Println("stu Say(11111)")
}
//使用方法
func main(){
var stu Stu
//不是“接口实例化结构体”,而是:👉 把一个“实现了接口的对象”赋值给接口变量
var a Ainterface =stu
/*
可以理解成:接口变量 a = (类型信息 + 数据) = (Stu + stu实例)
接口底层其实是:type interface {
type // 动态类型(这里是 Stu)
value // 实际数据(stu)
}
*/
/*调用时发生了什么?
Go 会:找到 a 里面的真实类型 👉 Stu 调用 Stu 的 Say()👉 这叫:动态分发(多态)
*/
a.Say()
}
//执行结构
// PS D:\golang\goproject\src\src01\go_code\src> go run chapter11\damo01\main.go
// stu Say(11111)
2)接口中所有的方法都没有方法体,即都是没有实现的方法
3)在Golang中,一个自定类型需要将某个接口的所有方法实现,就说这个自定义类型实现了该接口
5)只要是自定义数据类型,就可以实现接口,不仅仅是结构体类型
package main
import(
"fmt"
)
//定义接口这个接口的含义是:任何类型,只要实现了 Say() 方法,就属于 Ainterface
type Ainterface interface{
Say()
}
//定义结构体这一步非常关键:👉 Stu 已经“实现了”接口 Ainterface
type Stu struct{
Name string
}
//这一步非常关键:👉 Stu 已经“实现了”接口 Ainterface
func (stu Stu) Say(){
fmt.Println("stu Say(11111)")
}
type integer int
//自定义int 数据类型实现了 已经“实现了”接口 Ainterface
func (i integer) Say(){
fmt.Println("integer Say()")
}
//自定义切片 数据类型实现了 已经“实现了”接口 Ainterface
type intp []int
func (i intp)Say(){
fmt.Println("切片实现 Say()")
}
type intspec [3]int
func (i intspec)Say(){
fmt.Println("数组 Say()")
}
type maps map[int]int
func (i maps)Say(){
fmt.Println("集合实现Say")
}
//使用方法
func main(){
var stu Stu
//不是“接口实例化结构体”,而是:👉 把一个“实现了接口的对象”赋值给接口变量
var a Ainterface =stu
/*
可以理解成:接口变量 a = (类型信息 + 数据) = (Stu + stu实例)
接口底层其实是:type interface {
type // 动态类型(这里是 Stu)
value // 实际数据(stu)
}
*/
/*调用时发生了什么?
Go 会:找到 a 里面的真实类型 👉 Stu 调用 Stu 的 Say()👉 这叫:动态分发(多态)
*/
a.Say()
var t integer
var T Ainterface = t
T.Say()
var c intp
var C Ainterface =c
C.Say()
var b intspec
var B Ainterface =b
B.Say()
var d maps
var D Ainterface = d
D.Say()
}
//执行结构
// PS D:\golang\goproject\src\src01\go_code\src> go run chapter11\damo01\main.go
// stu Say(11111)
// integer Say()
// 切片实现 Say()
// 数组 Say()
// 集合实现Say
函数类型接口实现
package main
import(
"fmt"
)
type my func() //自定函数类型
func (i my)Say(){
fmt.Println("函数接口的实现")
i()
}
//使用方法
func main(){
var e my = my(func() {fmt.Println("你好e函数")})
var E Ainterface= e
E.Say()
}
//执行结构
// PS D:\golang\goproject\src\src01\go_code\src> go run chapter11\damo01\main.go
// 函数接口的实现
// 你好e函数
go 标准库也在用
这个设计在 Go 标准库里大量存在,比如:
👉 net/http 里的:
type HandlerFunc func(ResponseWriter, *Request)
func (f HandlerFunc) ServeHTTP(w ResponseWriter, r *Request) {
f(w, r)
}
👉 这就是为什么你可以直接写:
http.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) {
fmt.Println("hello")
})
总结
🧠 总结 ❌ 函数本身不能实现接口 ✅ 函数类型可以实现接口 本质: 👉 类型 + 方法 = 是否实现接口 🚀 给一个认知升级点(很重要) 现在可以把 Go 的接口理解为: 接口不是“面向对象”,而是“行为适配器” 而函数类型 + 接口 = 🔥 最轻量的设计模式(比 Java 优雅很多)
6) 一个自定类型可以实现多个接口
7)Golang 接口中不能有任何变量
8)一个接口(例如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 (a Stu)test01(){
fmt.Println("test01")
}
func (a Stu)test02(){
fmt.Println("test02")
}
func (a Stu)test03(){
fmt.Println("test03")
}
//如果要实现A接口需要把B接口方法和C接口方法全部实质再把A接口里特有的方法也一并实现才可以
func main(){
var A Stu
var s Ainterface= A //A的结构类型是stu ,这个类型实现了test01()test02()test03,所以可以直接实现Ainterface,
// 有因为Ainterface的接口继承Binterface和Cinterface 和自身的test03接口,所以这个Ainterface接口只能且必须匹配满足这3个方法的类型
s.test01()// test01 是C 接口的实现,因为A继承了C。所以可以调用
}
9)interface 类型默认是指针,如果没有初始化就是输出nil
10 )空接口interface{}没有任何方法。所以所有类型都实现了空接口
package main
import (
"fmt"
)
type Stu struct{
}
func (a Stu)test01(){
fmt.Println("test01")
}
func (a Stu)test02(){
fmt.Println("test02")
}
func (a Stu)test03(){
fmt.Println("test03")
}
type T interface{}
//如果要实现A接口需要把B接口方法和C接口方法全部实质再把A接口里特有的方法也一并实现才可以
func main(){
var A Stu
var t T = A //ok
fmt.Println(t)
var t2 interface{}
fmt.Println(t2)
var t3 interface{}= A //ok
fmt.Println(t3) //ok
}
// 执行结果
// PS D:\golang\goproject\src\src01\go_code\src> go run chapter11\demo02\main.go
// {}
// <nil>
// {}
两个接口有相同的方法,可以被一个接口同时继承,但是方法名参数不同则不可以被同一个接口继承
报错示例
package main
import (
"fmt"
//"src01/demo16/test"
)
type Binterface interface{
test01()
test03()
}
type Cinterface interface{
test02()
test03(n int) //这样就不可以了
}
type Ainterface interface{
Binterface
Cinterface
}
type Stu struct{
}
func (a Stu)test01(){
fmt.Println("test01")
}
func (a Stu)test02(){
fmt.Println("test02")
}
func (a Stu)test03(){
fmt.Println("test03")
}
type T interface{}
//如果要实现A接口需要把B接口方法和C接口方法全部实质再把A接口里特有的方法也一并实现才可以
func main(){
var A Stu
var s Ainterface= A //A的结构类型是stu ,这个类型实现了test01()test02()test03,所以可以直接实现Ainterface,
// // 有因为Ainterface的接口继承Binterface和Cinterface 和自身的test03接口,所以这个Ainterface接口只能且必须匹配满足这3个方法的类型
s.test03()// test01 是C 接口的实现,因为A继承了C。所以可以调用
}
// 执行结果
// PS D:\golang\goproject\src\src01\go_code\src> go run chapter11\demo02\main.go
// # command-line-arguments
// chapter11\demo02\main.go:17:2: duplicate method test03
// chapter11\demo02\main.go:16:2: other declaration of test03
体现
package main
import (
"fmt"
"math/rand"
"sort"
)
type Hero struct{
Name string
Age int
}
type HeroSlice []Hero
func (hs HeroSlice)Len() int{
return len(hs)
}
// 按年龄从到大
func (hs HeroSlice)Less(i,j int) bool{
return hs[i].Age>hs[j].Age
}
func (hs HeroSlice)Swap(i,j int){
hs[i],hs[j]=hs[j],hs[i]
}
func main(){
var intSlice = []int{1,9,0,-1,7,90}
// 排序
//使用系统
fmt.Println(intSlice)
sort.Ints(intSlice)
fmt.Println(intSlice)
var t HeroSlice
//t = make([]Hero, 10)
for i:=0;i<10;i++{
hero := Hero{
Name: fmt.Sprintf("英雄~%d",rand.Intn(100)),
Age: rand.Intn(50+20),
}
t=append(t, hero)
}
fmt.Println(t)
// for _,v := range t{
// fmt.Println(v)
// }
sort.Sort(t)
fmt.Println(t)
}
// 执行结过
// PS D:\golang\goproject\src\src01\go_code\src> go run chapter11\demo03\main.go
// [1 9 0 -1 7 90]
// [-1 0 1 7 9 90]
// [{英雄~73 65} {英雄~43 16} {英雄~6 57} {英雄~19 23} {英雄~96 48} {英雄~27 67} {英雄~5 47} {英雄~20 55} {英雄~12 46} {英雄~55 28}]
// [{英雄~43 16} {英雄~19 23} {英雄~55 28} {英雄~12 46} {英雄~5 47} {英雄~96 48} {英雄~20 55} {英雄~6 57} {英雄~73 65} {英雄~27 67}]
接口继承

示例
package main
import (
"fmt"
// "math/rand"
// "sort"
)
type BirdAble interface{
Flying()
}
type fish interface{
Fish()
}
type Monkey struct{
Name string
}
func (this *Monkey)climbing(){
fmt.Println("爬树")
}
type LittleMonkey struct{
Monkey
}
func (t *LittleMonkey)Flying(){
fmt.Println(t.Name,"学会鸟的飞行")
}
func (t *LittleMonkey)Fish(){
fmt.Println(t.Name,"学会了游泳")
}
func main(){
monkey:=LittleMonkey{Monkey{Name: "悟空"}}
monkey.climbing()
var B BirdAble = &monkey
B.Flying()
var S fish =&monkey
S.Fish()
}
// 执行结过
// PS D:\golang\goproject\src\src01\go_code\src> go run chapter11\demo04\main.go
// 爬树
// 悟空 学会鸟的飞行
// 悟空 学会了游泳
1)当A结构体继承B结构体后那么A结构体就拥有B结构体所有方法与字段,并可以在此基础上扩展自己新有的字段及方法
2)A结构体扩展某个功能不会影响B结构体
接口可以看做是继承的补充
1. 接口继承解决的问题是不同的;
继承的价值主要在于:解决代码的复用性和可维护性
接口价值在于: 设计,设计好各种规范(方法),让其他自定义类型去实现这些方法
2.接口比继承灵活
接口比继承更加灵活,继承是满足is-a的关系,接口是like-a 的关系
3.接口在一定程度上实现了代码解耦性
面向对象编程 --- 多态
变量(实例)具有多种形态,面向对象的三大特征,在GO语言里,多态特征通过接口实现的,可以按照不同的接口来调用不同的实现,这时接口变量就呈现不同的形态
示例
package main
import(
"fmt"
)
//定义接口这个接口的含义是:任何类型,只要实现了 Say() 方法,就属于 Ainterface
type Ainterface interface{
Say()
}
//定义结构体这一步非常关键:👉 Stu 已经“实现了”接口 Ainterface
type Stu struct{
Name string
}
//这一步非常关键:👉 Stu 已经“实现了”接口 Ainterface
func (stu Stu) Say(){
fmt.Println("stu Say(11111)")
}
type integer int
//自定义int 数据类型实现了 已经“实现了”接口 Ainterface
func (i integer) Say(){
fmt.Println("integer Say()")
}
//自定义切片 数据类型实现了 已经“实现了”接口 Ainterface
type intp []int
func (i intp)Say(){
fmt.Println("切片实现 Say()")
}
type intspec [3]int
func (i intspec)Say(){
fmt.Println("数组 Say()")
}
type maps map[int]int
func (i maps)Say(){
fmt.Println("集合实现Say")
}
type my func()
func (i my)Say(){
fmt.Println("函数接口的实现")
i()
}
//使用方法
func main(){
var stu Stu
//不是“接口实例化结构体”,而是:👉 把一个“实现了接口的对象”赋值给接口变量
var a Ainterface =stu
/*
可以理解成:接口变量 a = (类型信息 + 数据) = (Stu + stu实例)
接口底层其实是:type interface {
type // 动态类型(这里是 Stu)
value // 实际数据(stu)
}
*/
/*调用时发生了什么?
Go 会:找到 a 里面的真实类型 👉 Stu 调用 Stu 的 Say()👉 这叫:动态分发(多态)
*/
a.Say()
var t integer
var T Ainterface = t
T.Say()
var c intp
var C Ainterface =c
C.Say()
var b intspec
var B Ainterface =b
B.Say()
var d maps
var D Ainterface = d
D.Say()
var e my = my(func() {fmt.Println("你好e函数")})
var E Ainterface= e
E.Say()
}
//执行结构
// PS D:\golang\goproject\src\src01\go_code\src> go run chapter11\damo01\main.go
// stu Say(11111)
// integer Say()
// 切片实现 Say()
// 数组 Say()
// 集合实现Say
// 函数接口的实现
// 你好e函数
接口体现出多态
1)多态参数
在USB的案例中,USB usb,即可以接受手机变量,又可以接收相机变量,就体现了Usb接口多态
2)多态数组
给Usb 数组中,存放Phone结构体和Camera 结构体变量,Phone还有一个特有的方法call(),请遍历Usb 数组结构体,除了调用Usb接口声明的方法,还需要调用Phone特有方法
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 Camer struct{
Name string
}
func (p Camer)Start(){
fmt.Println("相机开始工作")
}
func (p Camer)Stop(){
fmt.Println("相机停止工作")
}
type Compose struct{
}
//此方法接收一个Usb 接口类型变量;只要实现了Usb方法;就是指实现了USB接口声明的所有方法
func (p Compose)Working(usb Usb){
usb.Start()
usb.Stop()
}
func main(){
//定义一个Usb接口数组,可以存放Phone和Camera 的结构体变量;体现多态数组
var usbArr [3]Usb
usbArr[0]=Phone{"华为"}
usbArr[1]=Phone{"小米"}
usbArr[2]=Camer{"佳能"}
fmt.Println(usbArr)
}
// PS D:\golang\goproject\src\src01\go_code\src> go run chapter11\demo06\main.go
// [{华为} {小米} {佳能}]
断言
package main
import(
"fmt"
)
type Point struct{
x int
y int
}
func main(){
var a interface{}
var P Point= Point{1,3}
a = P
var b Point
b = a //不允许
fmt.Println(b)
}
// 执行结果
// PS D:\golang\goproject\src\src01\go_code\src> go run chapter11\demo06\main.go
// # command-line-arguments
// chapter11\demo06\main.go:14:6: cannot use a (variable of type interface{}) as Point value in assignment: need type assertion
/*
a=b
为啥这一行报错一句话结论👉 在 Go 里:
interface → 具体类型,必须做“类型断言”
var a interface{}
var p Point = Point{1,3}
a = p
a = (动态类型: Point, 值: {1,3})
interface 里面装的是:类型 + 值
2️⃣ 为什么不能直接赋值?
var b Point
b = a // ❌ 报错
因为:
👉 编译器只看到:
a 是 interface{}
👉 它不知道里面到底是:
Point ❓
int ❓
string ❓
👉 不安全 → 禁止
🔥 3️⃣ 正确写法(类型断言)
写法1(安全)
b, ok := a.(Point)
if ok {
fmt.Println(b)
}
写法2(你确定类型时)
b := a.(Point)
fmt.Println(b)
👉 如果不是 Point → 直接 panic
🔥 4️⃣ 为什么必须这样设计?
但取出来时:
👉 必须明确告诉编译器你要什么类型
6️⃣ 一个更高级的理解(很重要)
现在遇到的是:
👉 interface → concrete type
而 Go 允许反方向:
var p Point
var a interface{} = p // ✅ 自动
具体类型 → interface(自动)
interface → 具体类型(必须断言)
🧠 总结(核心记住)
interface 可以装任何类型
取出来必须用:类型断言 a.(T)
直接赋值不允许(不安全)
*/
报错解决方案
package main
import(
"fmt"
)
type Point struct{
x int
y int
}
func main(){
var a interface{}
var P Point= Point{1,3}
a = P
var b Point//没有实现接口
//b = a
//不允许
b = a.(Point)
fmt.Println(b)
}
引出类型断言
b = a.(Point)
与
b, ok := a.(Point)
if ok {
fmt.Println(b)
}
都是类型断言,表示判断a是不是执行Point类型的变量,如果是就转成Point类型赋值给b变量,否则报错
示例
package main
import(
"fmt"
)
type Point struct{
x int
y int
}
func main(){
var a interface{}
var P Point= Point{1,3}
a = P
var b Point
//b = a
//不允许
b = a.(Point)//表示把a 转换成Point
fmt.Println(b)
var x interface{}
var t Point = Point{45,78}
x = t //空接口可以介绍任意类型
//var z Point
z,ok:=x.(Point)//带检查的类型断言
if ok{
fmt.Println("z 的值",z,ok)
}
var q interface{}
var w float32 = 89
var e float32
q=w
e=q.(float32)
fmt.Println("e的值",e)
var r int
r = int(q.(float32))//两次转换
fmt.Println("int类型的r=",r)
}
// 执行结果
// {1 3}
// z 的值 {45 78} true
// e的值 89
// int类型的r= 89在
在进行类型断言时确保,如果类型不匹配会报错
带检测机制的断言,如果成功就成功失败就算了,否则也不要报panic
package main
import (
"fmt"
//v2 "math/rand/v2"
)
type Point struct{
x int
y int
}
func main(){
var a interface{}
var P Point= Point{1,3}
a = P
var b Point
b,ok := a.(Point)//表示把a 转换成Point
if ok {
fmt.Println("b成功",b)
}else{
fmt.Println("类型不匹配转换失败",a)
}
// fmt.Println(b)
}
// 执行结果
// PS D:\golang\goproject\src\src01\go_code\src> go run chapter11\demo06\main.go
// b成功 {1 3}
// {1 3}
简洁写法
package main
import (
"fmt"
//v2 "math/rand/v2"
)
type Point struct{
x int
y int
}
func main(){
var a interface{}
var P Point= Point{1,3}
a = P
if b,ok:= a.(Point); ok {
fmt.Println("b成功",b)
}else{
fmt.Println("类型不匹配转换失败",a)
}
}
// 执行结果
// PS D:\golang\goproject\src\src01\go_code\src> go run chapter11\demo06\main.go
// b成功 {1 3}
一个进阶提示
switch v := a.(type) {
case Point:
fmt.Println("是Point", v)
case int:
fmt.Println("是int", v)
}
断言的最佳实践
package main
import (
"fmt"
//"internal/syscall/windows"
)
//定义接口
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 Camer struct{
Name string
}
func (p Camer)Start(){
fmt.Println("相机开始工作")
}
func (p Camer)Stop(){
fmt.Println("相机停止工作")
}
type Compose struct{
}
//此方法接收一个Usb 接口类型变量;只要实现了Usb方法;就是指实现了USB接口声明的所有方法
func (p Compose)Working(usb Usb){
usb.Start()
//类型断言应用
if t,ok:=usb.(Phone);ok{
t.Call()
}
usb.Stop()
}
func main(){
//定义一个Usb接口数组,可以存放Phone和Camera 的结构体变量;体现多态数组
var usbArr [3]Usb
usbArr[0]=Phone{"华为"}
usbArr[1]=Phone{"小米"}
usbArr[2]=Camer{"佳能"}
fmt.Println(usbArr)
var c Compose
// var t Phone
for _,v:=range usbArr{
c.Working(v)
fmt.Println()
}
}
// PS D:\golang\goproject\src\src01\go_code\src> go run chapter11\demo07\main.go
// [{华为} {小米} {佳能}]
// 手机开始工作
// 手机打电话
// 手机停止工作
// 手机开始工作
// 手机打电话
// 手机停止工作
// 相机开始工作
// 相机停止工作
断言实践2
package main
import (
"fmt"
//"go/types"
//"internal/syscall/windows"
)
func TypeJudge(it ...interface{}){
for i,x :=range it{
switch x.(type){//这里是判断type是固定写法
case bool:
fmt.Printf("param #%d is a bool 值是%v\n",i,x)
case float64:
fmt.Printf("param #%d is a float64 值是%v\n",i,x)
case int, int64:
fmt.Printf("param #%d is a int 值是%v\n",i,x)
case nil:
fmt.Printf("param #%d is a nil 值是%v\n",i,x)
case string:
fmt.Printf("param #%d is a string 值是%v\n",i,x)
default:
fmt.Printf("param #%d's type is unknown 值是%v\n",i,x)
}
}
}
func main(){
t := 6
TypeJudge(t)
r := "65"
TypeJudge(r)
}
// PS D:\golang\goproject\src\src01\go_code\src> go run chapter11\demo07\main.go
// param #0 is a int 值是6
// param #0 is a string 值是65
示例3 新加地址类型判断
package main
import (
"fmt"
//"go/types"
//"internal/syscall/windows"
)
func TypeJudge(it ...interface{}){
for i,x :=range it{
switch x.(type){//这里是判断type是固定写法
case bool:
fmt.Printf("param #%d is a bool 值是%v\n",i,x)
case float64:
fmt.Printf("param #%d is a float64 值是%v\n",i,x)
case int, int64:
fmt.Printf("param #%d is a int 值是%v\n",i,x)
case nil:
fmt.Printf("param #%d is a nil 值是%v\n",i,x)
case string:
fmt.Printf("param #%d is a string 值是%v\n",i,x)
case *float64:
fmt.Printf("param #%d is a *float64 值是%v\n",i,x)
case *int,*int64:
fmt.Printf("param #%d is a *int 值是%v\n",i,x)
case *string:
fmt.Printf("param #%d is a *string 值是%v\n",i,x)
default:
fmt.Printf("param #%d's type is unknown 值是%v\n",i,x)
}
}
}
func main(){
t := 6
//var y interface{}=t
TypeJudge(t)
r := "65"
TypeJudge(r)
e :="你好"
w := &e
TypeJudge(w)
}
// PS D:\golang\goproject\src\src01\go_code\src> go run chapter11\demo07\main.go
// param #0 is a int 值是6
// param #0 is a string 值是65
// param #0 is a *string 值是0xc000026080

浙公网安备 33010602011771号