使用接口:
接口声明的格式形式代码如下:
type 接口类型名 interface{
方法名1 ( 参数列表1 ) 返回值列表1
方法名2 ( 参数列表2 ) 返回值列表2
……
}
接口类型名:使用type将接口定义为自定义的类型名。Go语言的接口在命名时,一般会在单词后面添加er,如有写操作的接口叫Writer,有字符串功能的接口叫Stringer,有关闭功能的接口叫Closer等。
方法名:当方法名首字母是大写时,且这个接口类型名首字母也是大写时,这个方法可以被接口所有的包(package)之外的代码访问。
参数列表、返回值列表:参数列表和返回值列表中的参数变量名可以被忽略。
goroutine和channel是支撑起Go语言的并发模型的基石。
格式定义接口,如下:
type Namer interface{
Method1(param_list) return_type
Method2(param_list) return_type
...
}
Namer是一个接口类型。
使用接口Robot,程序清单如下:
package main
import(
"fmt"
"errors"
)
type Robot interface{
PowerOn() error
}
type T850 struct{
Name string
}
func(a *T850)PowerOn()error{
return nil
}
type R2D2 struct{
Broken bool
}
func (r *R2D2)PowerOn()error{
if r.Broken{
return errors.New("R2D2 is broken")
}else{
return nil
}
}
func Boot(r Robot)error{
return r.PowerOn()
}
func main(){
t:=T850{
Name:"The Terminator",
}
r:=R2D2{
Broken:true,
}
err:=Boot(&r)
if err!=nil{
fmt.Println(err)
}else{
fmt.Println("Robot is powered on!")
}
err=Boot(&t)
if err!=nil{
fmt.Println(err)
}else{
fmt.Println("Robot is powered on!")
}
}
运行结果如下:
R2D2 is broken
Robot is powered on!
代码如下:
package main
import (
"fmt"
)
type Shaper interface {
Area() float32
}
type Square struct {
side float32
}
func (sq *Square) Area() float32 {
return sq.side * sq.side
}
func main() {
sq1 := new(Square)
sq1.side = 5
areaIntf := sq1
fmt.Printf("面积为: %f\n", areaIntf.Area())
}
运行结果如下:
面积为: 25.000000
代码如下:
package main
import (
"fmt"
)
type Shaper interface {
Area() float32
}
type Square struct {
side float32
}
func (sq *Square) Area() float32 {
return sq.side * sq.side
}
type Rectangle struct {
length, width float32
}
func (r Rectangle) Area() float32 {
return r.length * r.width
}
func main() {
r := Rectangle{5, 3}
q := &Square{5}
shapes := []Shaper{r, q}
for n, _ := range shapes {
fmt.Println("形状参数:", shapes[n])
fmt.Println("形状面积是:", shapes[n].Area())
}
}
运行结果如下:
形状参数: {5 3}
形状面积是: 15
形状参数: &{5}
形状面积是: 25
代码如下:
package main
import (
"fmt"
)
type stockPosition struct {
ticker string
sharePrice float32
count float32
}
func (s stockPosition) getValue() float32 {
return s.sharePrice * s.count
}
type car struct {
make string
model string
price float32
}
func (c car) getValue() float32 {
return c.price
}
type valuable interface {
getValue() float32
}
func showValue(asset valuable) {
fmt.Printf("资产的价值是: %f\n", asset.getValue())
}
func main() {
var o valuable = stockPosition{"GOOG", 577.20, 4}
showValue(o)
o = car{"BMW", "M3", 66500}
showValue(o)
}
运行结果如下:
资产的价值是: 2308.800049
资产的价值是: 66500.000000
接口类型和约定
1.动态类型
代码如下:(没看懂)
package main
import (
"fmt"
"math"
)
type Square struct {
side float32
}
type Circle struct {
radius float32
}
type Shaper interface {
Area() float32
}
func (sq *Square) Area() float32 {
return sq.side * sq.side
}
func (ci *Circle) Area() float32 {
return ci.radius * ci.radius * math.Pi
}
func main() {
var areaIntf Shaper
sq1 := new(Square)
sq1.side = 5
areaIntf = sq1
if t, ok := areaIntf.(*Square); ok {
fmt.Printf("areaIntf的类型是:%T\n", t)
}
if u, ok := areaIntf.(*Circle); ok {
fmt.Printf("areaIntf的类型是:%T\n", u)
} else {
fmt.Println("areaIntf不含类型为Circle的变量")
}
}
运行结果如下:
areaIntf的类型是:*main.Square
areaIntf不含类型为Circle的变量
2.类型判断
接口实现
嵌套接口
接口赋值
接口查询
接口组合
实现接口的条件:
接口被实现的条件一:接口的方法与实现接口的类型方法格式一致
代码如下:
package main
import (
"fmt"
)
//定义一个数据写入器
type DataWriter interface {
WriteData(data interface{}) error
}
//定义文件结构,用于实现DataWriter
type file struct {
}
//实现DataWriter接口的WriteData()方法
func (d *file) WriteData(data interface{}) error {
//模拟写入数据
fmt.Println("WriteData:", data)
return nil
}
func main() {
//实例化file
f := new(file)
//声明一个DataWriter的接口
var writer DataWriter
//将接口赋值f,也就是*file类型
writer = f
//使用DataWriter接口进行数据写入
writer.WriteData("data")
}
运行结果如下:
WriteData: data
条件二:接口中所有方法均被发现
理解类型与接口的关系
一、一个类型可以实现多个接口
二、多个类型可以实现相同的接口
示例:便于扩展输出方式的日志系统
1、日志对外接口
日志写入器,logger.go的代码如下:
package main
//声明日志写入器接口
type LogWriter interface {
Write(data interface{}) error
}
//日志器
type Logger struct {
//这个日志器用到的日志写入器
writerList []LogWriter
}
//注册一个日志写入器
func (l *Logger) RegisterWriter(writer LogWriter) {
l.writerList = append(l.writerList, writer)
}
//将一个data类型的数据写入日志
func (l *Logger) Log(data interface{}) {
//遍历所有注册的写入器
for _, writer := range l.writerList {
//将日志输出到每一个写入器中
writer.Write(data)
}
}
//创建日志器的实例
func NewLogger() *Logger {
return &Logger{}
}
2、文件写入器
file.go的代码如下:
package main
import (
"errors"
"fmt"
"os"
)
//声明文件写入器
type fileWriter struct {
file *os.File
}
//设置文件写入器写入的文件名
func (f *fileWriter) SetFile(filename string) (err error) {
//如果文件已经打开,关闭前一个谁的
if f.file != nil {
f.file.Close()
}
//创建一个文件并保存文件句柄
f.file, err = os.Create(filename)
//如果创建的过程出现错误,则返回错误
return err
}
//实现LogWriter的Write()方法
func (f *fileWriter) Write(data interface{}) error {
//日志文件可能没有创建成功
if f.file == nil {
//日志文件没有准备好
return errors.New("file not created")
}
//将数据序列化为字符串
str := fmt.Sprintf("%v\n", data)
//将数据以字节数组写入文件中
_, err := f.file.Write([]byte(str))
return err
}
//创建文件写入器实例
func newFileWriter() *fileWriter {
return &fileWriter{}
}
3、命令行写入器
console.go的代码如下:
package main
import (
"fmt"
"os"
)
//命令行写入器
type consoleWriter struct {
}
//实现LogWriter的Write()方法
func (f *consoleWriter) Write(data interface{}) error {
//将数据序列化为字符串
str := fmt.Sprintf("%v\n", data)
//将数据以字节数组写入命令行中
_, err := os.Stdout.Write([]byte(str))
return err
}
//创建命令行写入器实例
func newConsoleWriter() *consoleWriter {
return &consoleWriter{}
}
4、使用日志
main.go的代码如下:
package main
import "fmt"
func createLogger() *Logger {
//创建日志器
l := NewLogger()
//创建命令行写入器
cw := newConsoleWriter()
//注册命令行写入器到日志器中
l.RegisterWriter(cw)
//创建文件写入器
fw := newFileWriter()
//设置文件名
if err := fw.SetFile("log.log"); err != nil {
fmt.Println(err)
}
//注册文件写入器到日志器中
l.RegisterWriter(fw)
return 1
}
func main() {
//准备日志器
l := createLogger()
//写一个日志
l.Log("Hello")
}
示例:使用接口进行数据的排序
一、使用sort.Interface接口进行排序
sortstring.go,字符串排序,代码如下:
package main
import (
"fmt"
"sort"
)
//将[]string定义为MyStringList类型
type MyStringList []string
//实现sort.Interface接口的获取元素数量方法
func (m MyStringList) Len() int {
return len(m)
}
//实现sort.Interface接口的比较元素方法
func (m MyStringList) Less(i, j int) bool {
return m[i] < m[j]
}
//实现sort。Interface接口的交换元素方法
func (m MyStringList) Swap(i, j int) {
m[i], m[j] = m[j], m[i]
}
func main() {
//准备一个内容被打乱顺序的字符串切片
names := MyStringList{
"3. Triple Kill",
"5. Penta Kill",
"2. Double Kill",
"4. Quadra Kill",
"1. First Blood",
}
//使用sort包进行排序
sort.Sort(names)
//遍历打印结果
for _, v := range names {
fmt.Printf("%s\n", v)
}
}
运行结果如下:
1. First Blood
2. Double Kill
3. Triple Kill
4. Quadra Kill
5. Penta Kill
二、常见类型的便捷排序
1、字符串切片的便捷排序
2、对整形切片进行排序
3、sort包内建的类型排序接口一览
sort包中内建的类型排序接口
类型 | 实现sort.Interface的类型 | 直接排序方法 | 说明 |
字符口中(String) | StringSlice | sort.Strings(a []string) | 字符ASCII值升序 |
整形(int) | IntSlice | sort.Ints(a []int) | 数值升序 |
双精度浮点(float64) | Float64Slice | sort.Float64s(a []float64) | 数值升序 |
三、对结构体数据进行排序
1、完整实现sort.Interface进行结构体排序
代码如下:
package main
import (
"fmt"
"sort"
)
//声明英雄的分类
type HeroKind int
//定义HeroKind常量,类似于枚举
const (
None HeroKind = iota
Tank
Assassin
Mage
)
//定义英雄名单的结构
type Hero struct {
Name string //英雄的名字
Kind HeroKind //英雄的种类
}
//将英雄指针的切片定义为Heros类型
type Heros []*Hero
//实现sort.Interface接口取元素数量方法
func (s Heros) Len() int {
return len(s)
}
//实现sort.Interface接口比较元素方法
func (s Heros) Less(i, j int) bool {
//如果英雄的分类不一致时,优先对分类进行排序
if s[i].Kind != s[j].Kind {
return s[i].Kind < s[j].Kind
}
//默认按英雄名字字符升序排列
return s[i].Name < s[j].Name
}
//实现sort.Interface接口交换元素方法
func (s Heros) Swap(i, j int) {
s[i], s[j] = s[j], s[i]
}
func main() {
//准备英雄列表
heros := Heros{
&Hero{"吕布", Tank},
&Hero{"李白", Assassin},
&Hero{"妲己", Mage},
&Hero{"貂蝉", Assassin},
&Hero{"关羽", Tank},
&Hero{"诸葛亮", Mage},
}
//使用sort包进行排序
sort.Sort(heros)
//遍历英雄列表打印排序结果
for _, v := range heros {
fmt.Printf("%+v\n", v)
}
}
运行结果如下:
&{Name:关羽 Kind:1}
&{Name:吕布 Kind:1}
&{Name:李白 Kind:2}
&{Name:貂蝉 Kind:2}
&{Name:妲己 Kind:3}
&{Name:诸葛亮 Kind:3}
2、使用sort.Slice进行切片元素排序
代码如下:
package main
import (
"fmt"
"sort"
)
//声明英雄的分类
type HeroKind int
//定义HeroKind常量,类似于枚举
const (
None HeroKind = iota
Tank
Assassin
Mage
)
//定义英雄名单的结构
type Hero struct {
Name string //英雄的名字
Kind HeroKind //英雄的种类
}
func main() {
//准备英雄列表
heros := []*Hero{
&Hero{"吕布", Tank},
&Hero{"李白", Assassin},
&Hero{"妲己", Mage},
&Hero{"貂蝉", Assassin},
&Hero{"关羽", Tank},
&Hero{"诸葛亮", Mage},
}
sort.Slice(heros, func(i, j int) bool {
if heros[i].Kind != heros[j].Kind {
return heros[i].Kind < heros[j].Kind
}
return heros[i].Name < heros[j].Name
})
//遍历英雄列表打印排序结果
for _, v := range heros {
fmt.Printf("%+v\n", v)
}
}
运行结果如下:
&{Name:关羽 Kind:1}
&{Name:吕布 Kind:1}
&{Name:李白 Kind:2}
&{Name:貂蝉 Kind:2}
&{Name:妲己 Kind:3}
&{Name:诸葛亮 Kind:3}
接口的嵌套组合——将多个接口放在一个接口内
在接口和类型间转换
一、类型断言的格式
类型断言的基本格式如下:
t:=i.(T)
i代表接口变量
T代表转换的目标类型
t代表转换后的变量
二、将接口转换为其他接口
鸟与猪,代码如下:
package main
import (
"fmt"
)
//定义飞行动物接口
type Flyer interface {
Fly()
}
//定义行走动物接口
type Walker interface {
Walk()
}
//定义鸟类
type bird struct{}
//实现飞行动物接口
func (b *bird) Fly() {
fmt.Println("bird: fly")
}
//为鸟添加Walk()方法,实现行走动物接口
func (b *bird) Walk() {
fmt.Println("bird: walk")
}
//定义猪
type pig struct{}
//为猪添加Walk()方法,实现行走动物接口
func (p *pig) Walk() {
fmt.Println("pig: walk")
}
func main() {
//创建动物的名字到实例的映射
animals := map[string]interface{}{
"bird": new(bird),
"pig": new(pig),
}
//遍历映射
for name, obj := range animals {
//判断对象是否为飞行动物
f, isFlyer := obj.(Flyer)
//判断对象是否为行走动物
w, isWalker := obj.(Walker)
fmt.Printf("name: %s isFlyer:%v isWalker: %v\n", name, isFlyer, isWalker)
//如果是飞行动物则调用飞行动物接口
if isFlyer {
f.Fly()
}
//如果是行走动物则调用行走动物接口
if isWalker {
w.Walk()
}
}
}
运行结果如下:
name: bird isFlyer:true isWalker: true
bird: fly
bird: walk
name: pig isFlyer:false isWalker: true
pig: walk
书上的运行结果如下:
name: pig isFlyer:false isWalker: true
pig: walk
name: bird isFlyer:true isWalker: true
bird: fly
bird: walk
三、将接口转换为其它类型
空接口类型()——能保存所有值的类型
一、将值保存到空接口
代码如下:
package main
import (
"fmt"
)
func main() {
var any interface{}
any = 1
fmt.Println(any)
any = "hello"
fmt.Println(any)
any = false
fmt.Println(any)
}
运行结果如下:
1
hello
false
二、从空接口获取值
代码如下:
package main
import (
"fmt"
)
func main() {
//声明a变量,类型int,初始值为1
var a int = 1
//声明i变量,类型为interface{},初始值为a,此时i的值变为1
var i interface{} = a
//声明b变量,尝试赋值i
var b int = i.(int)
fmt.Println(b)
}
运行结果如下:
1
三、空接口的值比较
1、类型不同的空接口间的比较结果不相同
代码如下:
package main
import (
"fmt"
)
func main() {
//a保存整形
var a interface{} = 100
//b保存字符串
var b interface{} = "hi"
//两个空接口不相等
fmt.Println(a == b)
}
运行结果如下:
false
2、不能比较空接口中的动态值
类型的可比较性
类型 | 说明 |
map | 宕机错误,不可比较 |
切片([]T) | 宕机错误,不可比较 |
通道(channel) | 可比较,必须由同一个make生成,也就是同一个通道才会是true,否则为false |
数组([容量]T) | 可比较,编译期知道两个数组是否一致 |
结构体 | 可比较,可以逐个比较结构体的值 |
函数 | 可比较 |
示例:使用空接口实现可以保存任意值的字典
代码如下:
package main
import (
"fmt"
)
//字典结构
type Dictionary struct {
data map[interface{}]interface{} //键值都为interface{}类型
}
//根据键获取值
func (d *Dictionary) Get(key interface{}) interface{} {
return d.data[key]
}
//设置键值
func (d *Dictionary) Set(key interface{}, value interface{}) {
d.data[key] = value
}
//遍历所有的键值,如果回调返回值为false,停止遍历
func (d *Dictionary) Visit(callback func(k, v interface{}) bool) {
if callback == nil {
return
}
for k, v := range d.data {
if !callback(k, v) {
return
}
}
}
//清空所有的数据
func (d *Dictionary) Clear() {
d.data = make(map[interface{}]interface{})
}
//创建一个字典
func NewDictionary() *Dictionary {
d := &Dictionary{}
//初始化map
d.Clear()
return d
}
func main() {
//创建字典实例
dict := NewDictionary()
//添加游戏数据
dict.Set("My Factory", 60)
dict.Set("Terra Craft", 36)
dict.Set("Don't Hungry", 24)
//获取值及打印值
favorite := dict.Get("Terra Craft")
fmt.Println("favorite:", favorite)
//遍历所有的字典元素
dict.Visit(func(key, value interface{}) bool {
//将值转为int类型,并判断 是否大于40
if value.(int) > 40 {
//输出“很贵”
fmt.Println(key, "is expensive")
return true
}
//默认都是输出“很便宜”
fmt.Println(key, "is cheap")
return true
})
}
运行结果如下:
favorite: 36
My Factory is expensive
Terra Craft is cheap
Don't Hungry is cheap
类型分支——批量判断空接口中变量的类型
一、类型断言的书写格式
switch实现类型分支时的写法格式如下:
switch 接口变量.(type){
case 类型1:
//变量是类型1时的处理
case 类型2:
//变量是类型2时的处理
……
default:
//变量不是所有case中列举的类型时的处理
}
二、使用类型分支判断基本类型
代码如下:
package main
import (
"fmt"
)
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")
}
}
func main() {
printType(1024)
printType("pig")
printType(true)
}
运行结果如下:
1024 is int
pig is string
true is bool
三、使用类型分支判断接口类型
代码如下:
package main
import (
"fmt"
)
//电子支付方式
type Alipay struct {
}
//为Alipay添加CanUseFaceID()方法,表示电子支付方式支持刷脸
func (a *Alipay) CanUseFaceID() {
}
//现金支付方式
type Cash struct {
}
//Cash添加Stolen()方式,表示现金支付方式会出现 偷窥情况
func (a *Cash) Stolen() {
}
//具备刷脸特性的接口
type CantainCanUseFaceID interface {
CanUseFaceID()
}
//具备被偷特性的接口
type ContainStolen interface {
Stolen()
}
//打印支付方式具备的特点
func print(payMethod interface{}) {
switch payMethod.(type) {
case CantainCanUseFaceID: //可以刷脸
fmt.Printf("%T can use faceid\n", payMethod)
case ContainStolen: //可能被偷
fmt.Printf("%T may be stolen\n", payMethod)
}
}
func main() {
//使用电子支付判断
print(new(Alipay))
//使用现金判断
print((new(Cash)))
}
运行结果如下:
*main.Alipay can use faceid
*main.Cash may be stolen
示例:实现有限状态机(FSM)
一、状态的概念
二、自定义状态需要实现的接口
状态接口,state.go,代码如下:
package main
import (
"reflect"
)
// 状态接口
type State interface {
// 获取状态名字
Name() string
// 该状态是否允许同状态转移
EnableSameTransit() bool
// 响应状态开始时
OnBegin()
// 响应状态结束时
OnEnd()
// 判断能否转移到某状态
CanTransitTo(name string) bool
}
// 从状态实例获取状态名
func StateName(s State) string {
if s == nil {
return "none"
}
// 使用反射获取状态的名称
return reflect.TypeOf(s).Elem().Name()
}
三、状态基本信息
状态信息,info.go,代码如下:
package main
// 状态的基础信息和默认实现
type StateInfo struct {
// 状态名
name string
}
// 状态名
func (s *StateInfo) Name() string {
return s.name
}
// 提供给内部设置名字
func (s *StateInfo) setName(name string) {
s.name = name
}
// 允许同状态转移
func (s *StateInfo) EnableSameTransit() bool {
return false
}
// 默认将状态开启时实现
func (s *StateInfo) OnBegin() {
}
// 默认将状态结束时实现
func (s *StateInfo) OnEnd() {
}
// 默认可以转移到任何状态
func (s *StateInfo) CanTransitTo(name string) bool {
return true
}
四、状态管理
状态管理器,statemgr.go,代码如下:
package main
import "errors"
// 状态没有找到的错误
var ErrStateNotFound = errors.New("state not found")
// 禁止在同状态间转移
var ErrForbidSameStateTransit = errors.New("forbid same state transit")
// 不能转移到指定状态
var ErrCannotTransitToState = errors.New("cannot transit to state")
// 状态管理器
type StateManager struct {
// 已经添加的状态
stateByName map[string]State
// 状态改变时的回调
OnChange func(from, to State)
// 当前状态
curr State
}
// 添加一个状态到管理器
func (sm *StateManager) Add(s State) {
// 获取状态的名称
name := StateName(s)
// 将s转换为能设置名字的接口,然后调用接口
s.(interface {
setName(name string)
}).setName(name)
// 根据状态名取已经添加的状态,检查是否已经存在
if sm.Get(name) != nil {
panic("duplicate state:" + name)
}
// 根据名字保存到map中
sm.stateByName[name] = s
}
// 根据名字获取指定状态
func (sm *StateManager) Get(name string) State {
if v, ok := sm.stateByName[name]; ok {
return v
}
return nil
}
// 获取当前的状态
func (sm *StateManager) CurrState() State {
return sm.curr
}
// 当前状态能否转移到目标状态
func (sm *StateManager) CanCurrTransitTo(name string) bool {
if sm.curr == nil {
return true
}
// 相同的不用转换
if sm.curr.Name() == name && !sm.curr.EnableSameTransit() {
return false
}
// 使用当前状态,检查能否转移到指定名字的状态
return sm.curr.CanTransitTo(name)
}
// 转移到指定状态
func (sm *StateManager) Transit(name string) error {
// 获取目标状态
next := sm.Get(name)
// 目标不存在
if next == nil {
return ErrStateNotFound
}
// 记录转移前的状态
pre := sm.curr
// 当前有状态
if sm.curr != nil {
// 相同的状态不用转换
if sm.curr.Name() == name && !sm.curr.EnableSameTransit() {
return ErrForbidSameStateTransit
}
// 不能转移到目标状态
if !sm.curr.CanTransitTo(name) {
return ErrCannotTransitToState
}
// 结束当前状态
sm.curr.OnEnd()
}
// 将当前状态切换为要转移到的目标状态
sm.curr = next
// 调用新状态的开始
sm.curr.OnBegin()
// 通知回调
if sm.OnChange != nil {
sm.OnChange(pre, sm.curr)
}
return nil
}
// 初始化状态管理器
func NewStateManager() *StateManager {
return &StateManager{
stateByName: make(map[string]State),
}
}
五、在状态间转移
六、自定义状态实现状态接口
一系列状态实现,main.go,代码如下:
package main
import (
"fmt"
)
// 闲置状态
type IdleState struct {
StateInfo // 使用StateInfo实现基础接口
}
// 重新实现状态开始
func (i *IdleState) OnBegin() {
fmt.Println("IdleState begin")
}
// 重新实现状态结束
func (i *IdleState) OnEnd() {
fmt.Println("IdleState end")
}
// 移动状态
type MoveState struct {
StateInfo
}
func (m *MoveState) OnBegin() {
fmt.Println("MoveState begin")
}
// 允许移动状态互相转换
func (m *MoveState) EnableSameTransit() bool {
return true
}
// 跳跃状态
type JumpState struct {
StateInfo
}
func (j *JumpState) OnBegin() {
fmt.Println("JumpState begin")
}
// 跳跃状态不能转移到移动状态
func (j *JumpState) CanTransitTo(name string) bool {
return name != "MoveState"
}
// 封装转移状态和输出日志
func transitAndReport(sm *StateManager, target string) {
if err := sm.Transit(target); err != nil {
fmt.Printf("FAILED! %s --> %s, %s\n\n", sm.CurrState().Name(), target, err.Error())
}
}
func main() {
// 实例化一个状态管理器
sm := NewStateManager()
// 响应状态转移的通知
sm.OnChange = func(from, to State) {
// 打印状态转移的流向
fmt.Printf("%s ---> %s\n\n", StateName(from), StateName(to))
}
// 添加3个状态
sm.Add(new(IdleState))
sm.Add(new(MoveState))
sm.Add(new(JumpState))
// 在不同状态间转移
transitAndReport(sm, "IdleState")
transitAndReport(sm, "MoveState")
transitAndReport(sm, "MoveState")
transitAndReport(sm, "JumpState")
transitAndReport(sm, "JumpState")
transitAndReport(sm, "IdleState")
}
七、使用状态机
运行结果如下:
PS C:\Users\a-xiaobodou\OneDrive - Microsoft\Projects\Go\fsm> go run main.go
# command-line-arguments
.\main.go:9:2: undefined: StateInfo
.\main.go:24:2: undefined: StateInfo
.\main.go:38:2: undefined: StateInfo
.\main.go:51:27: undefined: StateManager
.\main.go:59:8: undefined: NewStateManager
.\main.go:62:30: undefined: State
.\main.go:65:32: undefined: StateName
PS C:\Users\a-xiaobodou\OneDrive - Microsoft\Projects\Go\fsm>
代码如下:
package main
import (
"fmt"
)
type A struct {
Books int
}
type B interface {
f()
}
func (a A) f() {
fmt.Println("A.f() ", a.Books)
}
type I int
func (i I) f() {
fmt.Println("I.f() ", i)
}
func main() {
var a A = A{Books: 9}
a.f()
var b B = A{Books: 99} // 接口类型可接受结构体A的值,因为结构体A实现了接口
b.f()
var i I = 199 // I是int类型引申出来的新类型
i.f()
var b2 B = I(299) // 接口类型可接受新类型I的值,因为新类型I实现了接口
b2.f()
}
运行结果如下:
[Running] go run "c:\Users\a-xiaobodou\OneDrive - Microsoft\Projects\Go\main.go"
A.f() 9
A.f() 99
I.f() 199
I.f() 299
[Done] exited with code=0 in 8.338 seconds
代码如下:
package main
import (
"fmt"
)
type I interface {
f()
}
type T string
func (t T) f() {
fmt.Println("T Method")
}
type Stringer interface {
String() string
}
func main() {
// 类型断言
var varI I
varI = T("Tstring")
if v, ok := varI.(T); ok { // 类型断言
fmt.Println("varI类型断言结果为:", v) // varI已经转为T类型
varI.f()
}
// Type-switch做类型判断
var value interface{} // 默认为零值
switch str := value.(type) {
case string:
fmt.Println("value类型断言结果为string:", str)
case Stringer:
fmt.Println("value类型断言结果为Stringer:", str)
default:
fmt.Println("value类型不在上述类型之中")
}
// Comma-ok断言
value = "类型断言检查"
str, ok := value.(string)
if ok {
fmt.Printf("value类型断言结果为:%T\n", str) // str已经转为string类型
} else {
fmt.Printf("value不是string类型 \n")
}
}
运行结果如下:
[Running] go run "c:\Users\a-xiaobodou\OneDrive - Microsoft\Projects\Go\main.go"
varI类型断言结果为: Tstring
T Method
value类型不在上述类型之中
value类型断言结果为:string
[Done] exited with code=0 in 12.094 seconds
代码如下:
运行结果如下: