go 语言面向对象 下

vscode 常用快捷键

 

image

面向对象的三大特性

介绍

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)
}

  补充图

image

 

示例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)匿名结构体字段可以简化

image

示例

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 这样的关键字

应用场景

image

image

接口注意事项

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}]

  接口继承

image

示例

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

  

 

 

 

 

 

  

 

posted @ 2026-03-21 10:29  烟雨楼台,行云流水  阅读(2)  评论(0)    收藏  举报