第三章 容器(数组 切片 map 函数 结构体 指针 接口)

目录

一、 数组 切片 map

1.1 数组

1.1.1 数组定义

1.1.2 for range

1.2 切片slice

1.2.1 切片定义

1.2.2 切片使用

1.2.2.1 初始化

1.2.2.2 遍历

1.2.2.3 说明

1.3 map

1.3.1 基本语法

1.3.2 map的定义

1.3.3 map的初始化

1.3.4 map的遍历

1.3.5 增删查改

1.3.5.1 增/改

1.3.5.2 删

1.3.5.3 查

1.3.5.4 map长度

1.3.6 map切片

1.3.7 map的排序

1.3.8 map的应用举例

二、 函数

2.1 函数概念

2.2 匿名函数

2.3 闭包

2.4  init函数

2.5 defer锁(mutux)

2.6 内置函数

2.6.1 len new make

2.6.2 make

2.6.3 时间和日期相关函数

2.7 错误处理

2.7.1 基本说明

2.7.2 处理 error recover() panic()

三、 结构体

3.1 结构体声明

3.2 结构体使用

3.2.1 结构体定义

3.2.2 结构体初始化

3.2.3 结构体和切片组合

3.2.4 结构体和函数

3.2.5 嵌套匿名结构体

3.3 方法

3.3.1 介绍

3.3.2 方法的定义

3.3.3 方法的调用

3.4 type

四、 指针

4.1 指针

4.2 nil

五、 接口

5.1 基本语法

5.1.1 接口的定义

5.1.2 接口的实现

5.1.3 接口案例

5.2 指针为接受者

5.3 接口嵌套和多个接口实现

5.4 空接口

5.4.1 作为函数的参数

5.4.2 作为value的值

5.4.3 空接口类型断言


一、 数组 切片 map

1.1 数组

1.1.1 数组定义

① var 数组名 [数组大小]数据类型

② 数组的各个元素的地址间隔是依据数组的类型决定,比如 int64 -> 8        int32->4

③ 访问数组元素        数组名[下标]

④ 数组下标必须在指定范围内使用,否则报 panic:数组越界

⑤ 长度是数组类型的一部分,在传递函数参数时 需要考虑数组的长度

1.1.2 for range

① 遍历访问数组的元素

② for index, value := range array01 { }

package main
import "fmt"

func main(){
//数组的定义 var XXX [count]string
//数组的初始化
	//①
	var a = [3]string{"a", "b", "c"}
	fmt.Println(a)
	//②
	var b = [3]string{2: "asfs"}
	fmt.Println(b)
	//③
	var c = [...]string{"acv", "adaw", "edaf"} //常用
	fmt.Println(c)
//数组的遍历
	for _, value := range a {  //index不要了
		fmt.Println(value)
		}
//多维数组
	var nums1 [3][4]string //初始化
	//赋值
	nums1[0] = [4]string{"1", "2", "3", "4"} // 第一行四个元素
		//……
	nums1[2] = [4]string{"3", "4", "5", "6"}
	//遍历(3种)
	for i := 0; i < len(nums1); i++ {
		fmt.Println(nums1[i])
	}
	fmt.Println("--------------")
	for _, value := range nums1 {
		fmt.Println(value)
	}
	fmt.Println("--------------")
	for _, value1 := range nums1 {
		for _, value2 :=range value1{
				fmt.Print(value2 + " ")
		}
		fmt.Println()
	}
}

1.2 切片slice

① 切片是数组的一个引用,因此切片是引用类型,在进行传递时,遵守引用传递的机制

② 切片的使用和数组类似,遍历切片、访问切片的元素和求切片长度 len(slice)都一样

③ 切片的长度是可以变化的,因此切片是一个可以动态变化数组

1.2.1 切片定义

        var 切片名 [ ]类型

① slice 的确是一个引用类型
② slice 从底层来说,其实就是一个数据结构(struct 结构体)
        type slice struct {
        ptr          *[2]int
        len         int
        cap        int
        }

1.2.2 切片使用

1.2.2.1 初始化

法一:定义一个切片,然后让切片去引用一个已经创建好的数组

法二:通过 make 来创建切片 (最常用)

        基本语法:var 切片名 [ ]type = make([ ]type, len, [cap])

                type: 就是数据类型
                len : 大小
                cap :指定切片容量,可选, 如果你分配了 cap,则要求 cap>=len

        make也会创建一个数组。是由切片在底层进行维护

法三:定义一个切片,直接就指定具体数组,使用原理类似 make 的方式

1.2.2.2 遍历

切片的遍历和数组一样,也有两种方式
        for 循环常规方式遍历
        for-range 结构遍历切片

1.2.2.3 说明

① 切片初始化时,仍然不能越界。范围在 [0-len(arr)] 之间,但是可以动态增长

② cap 是一个内置函数,用于统计切片的容量,即最大可以存放多少个元素

③ 切片必须初始化

package main
import "fmt"

func main(){
//定义切片 var XXX []string
//初始化:1.直接从数组里面取  2.建立一个新的切片  3.make方法
	//①
	var a = [5]string{"1", "2", "3", "4", "5"}
	var b = a[0:2] //左开右闭( ]
	fmt.Println(b)

	//②
	var c = []string {"a", "c", "d"} // var c = []string {2: "afaef"}
	fmt.Println(c)

	//③
	d := make([]string, 3)
	d[0] = "1"
	d[2] = "5"
	fmt.Println(d)

//扩充
	var e = []string {"a", "c", "d"}
	e = append(e, "f", "d") //解释中的……表示后面可以选择性的加n项
	fmt.Println(e)

	var f = []string {"g", "h", "j"}
	e = append(e, f...)
	fmt.Println(e)

//删除d
	var g = []string {"a", "b", "c", "d", "e", "f", "g"}
	g =append(g[:3], g[4:]...)
	fmt.Println(g)

//切片的复制
	h := g[:]
	fmt.Println(h)

//copy
	var new = make([]string, len(h)) // 空间为0,不会动态分配空间
	copy(new, h)//前面是新的
	fmt.Println(new)
}

1.3 map

1.3.1 基本语法

map[keytype]valuetype

① map需要初始化(make)

② key-value是无序的

③ map是引用数据类型

④ map 的容量达到后,再向 map 增加元素,会自动扩容,并不会发生 panic,也就是说 map 能动态的增长 键值对(key-value)

⑤ map 的 value 也经常使用 struct 类型,更适合管理复杂的数据

1.3.2 map的定义

//定义   map是无序的,方便查询
	var mymap =  map[string]string{  //【序号的格式】内容的格式
		"go": "csgo", //有,号
	}
	fmt.Println(mymap)

1.3.3 map的初始化

①:

var test1 = map[string]string {}

②:

var test2 = make (map[string]string, 3)
test2["var"] = "var"
fmt.Println(test2["var"])
var map1 = map[string]string {
	"a": "b",
	"c": "d",
	"e": "f",
	"g": " ",
}

1.3.4 map的遍历

//map遍历:是无序的
for key, value := range studentMap {
	fmt.Println(key, value)
}
fmt.Println("--------------------")
for _, value := range studentMap {
	for _, value1 := range value {
		fmt.Println(value1)
	}
}

1.3.5 增删查改

1.3.5.1 增/改

如果 key 还没有,就是增加,如果 key 存在就是修改。

1.3.5.2 删

使用delete()内建函数从map中删除一组键值对,delete()函数的格式如下:

delete(map, key)

map:表示要删除键值对的map

key:表示要删除的键值对的键

如果要删除 map 的所有 key ,没有一个专门的方法一次删除,可以map = make(...),make 一个新的,让原来的成为垃圾,被 gc 回收(把map重新赋值)

1.3.5.3 查
	value, ok := map1["g"] // value表示map["g"]的元素   ok表示是否存在,返回bool类型
	fmt.Println(value, ok)
	if !ok {
		fmt.Println("false")
	}else {
		fmt.Println("ok" + value)
	}
1.3.5.4 map长度

len (Type)

1.3.6 map切片

切片的数据类型如果是 map,则我们称为 slice of map,map 切片,这样使用则 map 个数就可以动态变化了。

使用一个 map 来记录 monster 的信息 name 和 age, 也就是说一个 monster 对应一个 map,并且monster的个数可以动态的增加=>map 切片

package main
import "fmt"

func main() {
	var monsters []map[string]string
	monsters = make([]map[string]string, 2)
	if monsters[0] == nil {
		monsters[0] = make(map[string]string, 2)
		monsters[0]["name"] = "牛魔王"
		monsters[0]["age"] = "500"
	}
	if monsters[1] ==nil {
		monsters[1] = make(map[string]string, 2)
		monsters[1]["name"] = "狐狸精"
		monsters[1]["age"] = "5000"
	}
	//继续扩展 需要用append
	newMonster := map[string]string {
		"name" : "新时代妖怪",
		"age" : 	"100",
	}
	monsters = append(monsters, newMonster)
	fmt.Println(monsters)
}

1.3.7 map的排序

① golang 中的 map 默认是无序的
② golang 中 map 的排序,是先将 key 进行排序,然后根据 key 值遍历输出即可

	map1 := make(map[int]int, 10)
	map1[10] = 100
	map1[1] = 23
	map1[7] = 1
	map1[3] = 22
	fmt.Println(map1)

	var keys []int
	for k, _ := range map1 {
		keys = append(keys, k)
	}
	//排序
	sort.Ints(keys)
	fmt.Println(keys)

	for _, j := range keys {
		fmt.Printf("map1[%v]=%v\n",j,map1[j])
	}
}

1.3.8 map的应用举例

① 统计单词的出现次数:

//统计单词出现的次数
	var str = "how do you do"
	var strSlice = strings.Split(str, " ")
	fmt.Println(strSlice)
	
	var strMap = make(map[string]int)
	for _, v := range strSlice {
		strMap[v]++
	}
	fmt.Println(strMap) //map[do:2 how:1 you:1]
}

②:
        1) 使用 map[string]map[string]sting 的 map 类型
        2) key: 表示用户名,是唯一的,不可以重复
        3) 如果某个用户名存在,就将其密码修改"888888",如果不存在就增加这个用户信息,(包括昵称nickname 和 密码 pwd)。
        4) 编写一个函数 modifyUser(users map[string]map[string]string, name string) 完成上述功能

func modifyUser(users map[string]map[string]string, name string) {
	if users[name] != nil {
		users[name]["pwd"] = "888888"
	} else {
		users[name] = make(map[string]string, 2)
		users[name]["pwd"] = "888888"
		users[name]["nickname"] = "昵称" + name
	}
}
func main(){
	users := make(map[string]map[string]string)
	users["smith"] = make(map[string]string, 2)
	users["smith"]["pwd"] = "feasfeafwe"
	users["smith"]["nickname"] = "cat"

	modifyUser(users, "tom")
	modifyUser(users, "mary")
	modifyUser(users, "smith")
	fmt.Println(users)

}

二、 函数

2.1 函数概念

① 函数是一等公民

② 基本语法:

        func 函数名 (形式参数) (返回值列表)  { }

③ 定义一个函数:

        1.函数可以作为变量使用

        2.匿名函数,闭包函数

        3.接口 

④ 返回值列表也可以是多个        支持对函数返回值命名

⑤ 在 Go 中,函数也是一种数据类型,可以赋值给一个变量,则该变量就是一个函数类型的变量
了。通过该变量可以对函数调用

res := a(10, 40)

⑥ Go 函数不支持函数重载

⑦ Go 支持可变参数

func add2(desc string, items ...int)(sum int) { //前面括号是函数变量,后面是返回值 ...表示可以传进来任意个
	for _, value :=range items {
		sum += value;
	}
	return sum
}

2.2 匿名函数

① 在定义匿名函数时就直接调用,这种方式匿名函数只能调用一次。

res1 := func (n1 int, n2 int) int { 内容 }
func callback(y int, f func(int, int)) {
    f(y, 5)
}

② 全局匿名函数

Fun1 = func (n1 int, n2 int) int { } //Fun1就是一个全局匿名变量
res := Fun1(1, 4) //全局匿名函数调用

2.3 闭包

闭包就是一个函数和与其相关的引用环境组合的一个整体(实体)

(待学习)

package main
import "fmt"

//闭包:???、
func diejia() func() int {
	local :=0
	return func() int {
		local += 1
		return local
	}
}

func main(){
	nextfunc :=diejia()  //函数传递
	for i := 0; i<5; i++ {
		fmt.Println(nextfunc())
	}
	println("------------------")
	nextfunc2 :=diejia()
	for i := 0; i<3; i++ {
		fmt.Println(nextfunc2())
	}
}

2.4  init函数

每一个源文件都可以包含一个 init 函数,该函数会在 main 函数执行前,被 Go 运行框架调用,也就是说 init 会在 main 函数前被调用

② 如果一个文件同时包含全局变量定义,init 函数和 main 函数,则执行的流程全局变量定义->init
函数->main 函数

③ init 函数最主要的作用,就是完成一些初始化的工作

func init() {
    Age = 100
    Name = "Tom"
}

2.5 defer锁(mutux)

	var mu sync.Mutex
	mu.lock()
	defer mu.Unlock
	//内容……

① 连接数据库、打开文件、开始锁, 无论如何 最后都要记得去关闭数据库、关闭文件、解锁
为了在函数执行完毕后,及时的释放资源,Go 的设计者提供 defer (延时机制)。

② defer顺序: 栈(先进后出)

    执行到defer后,暂不执行,把defer后面的语句压入“defer栈”中

defer可以影响到返回值

package main
import "fmt"

func res () (re int) {
	defer func() {
		re++
	}()
	return 20
}
func main() {
	//defer顺序: =栈 先进后出
	defer fmt.Println("1")
	defer fmt.Println("2")
	fmt.Println("main")

	//defer可以影响到返回值
	fmt.Println(res())
}

2.6 内置函数

2.6.1 len new make

1) len:用来求长度,比如 string、array、slice、map、channel

2) new:用来分配内存,主要用来分配值类型,比如 int、float32,struct...返回的是指针

3) make:用来分配内存,主要用来分配引用类型,比如 channel、map、slice。

2.6.2 make

1、func make(Type, size IntegerType) Type
2、make 的返回类型与其参数相同,而非指向它的指针。其具体结果取决于具体的类型: 
        ① 切片: 因此make([]int, 0, 10) 会分配一个长度为0,容量为10的切片。
        ② 映射:初始分配的创建取决于size,但产生的映射长度为0。size可以省略,这种情况下就会分配一一个小的起始大小。
        ③ 通道:通道的缓存根据指定的缓存容里初始化。若size为零或被省略,该信道即为无缓存。

2.6.3 时间和日期相关函数

① time.Time 类型,用于表示时间

rec := time.Now()

② 执行时间

package main
import (
	"fmt"
	"strconv"
	"time"
)

func test() {
	str := ""
	for i := 0; i< 100000; i++ {
		str += "hello" + strconv.Itoa(i)
	}
}

func main() {
	//执行前,先获取当前的unix时间戳
	start := time.Now().Unix()
	test()
	end := time.Now().Unix()
	fmt.Printf("执行text()耗时间为%v秒\n", end-start)
}

2.7 错误处理

① 在默认情况下,当发生错误后(panic) ,程序就会退出(崩溃.)
② 如果我们希望:当发生错误后,可以捕获到错误,并进行处理,保证程序可以继续执行。还可以在捕获到错误后,给管理员一个提示(邮件,短信。。。)
③ 这里引出我们要将的错误处理机制

2.7.1 基本说明

①  Go 语言追求简洁优雅,所以,Go 语言不支持传统的 try…catch…finally 这种处理。
②  Go 中引入的处理方式为:defer, panic, recover
③ 这几个异常的使用场景可以这么简单描述:Go 中可以抛出一个 panic 的异常,然后在 defer 中通过 recover 捕获这个异常,然后正常处理

2.7.2 处理 error recover() panic()

① 使用 defer+recover 来处理错误

② errors.New("错误说明") , 会返回一个 error 类型的值,表示一个错误

③ panic 内置函数 ,接收一个 interface{}类型的值(也就是任何值)作为参数。可以接收 error 类
型的变量,输出错误信息,并退出程序

package main
import (
	"errors"
	"fmt"
)

func A() (int, error) {
	//多个defer会形成栈的顺序执行
	defer fmt.Printf("main1")

	//recover 需要在defer 之间
	defer func() {
		r := recover()
		if r != nil {
			fmt.Println("执行到了recover函数:", r)
		}
	}()

	//panic 会直接终止程序,不再往下执行,很少用
	defer panic("this is a panic")
	return 0, errors.New("this is a error")
}

func main() {
	if _,err := A(); err != nil { fmt.Println(err) }	
    //简写,是 _,err := A()        if err != nil
	//defer和普通程序的顺序
	fmt.Println("main外程序")
	defer fmt.Println("main外程序的defer")
}

三、 结构体

3.1 结构体声明

        type 结构体名称 struct {
                field1 type
                field2 type
        }

① 结构体是值类型 默认是值拷贝

② 结构体不需要初始化

③ 结构体的所有字段在内存中是连续的

3.2 结构体使用

3.2.1 结构体定义

type Person struct {
	name 	string
	age 	int
	addr 	string
	heigth 	int
}

3.2.2 结构体初始化

①:

person := Person{"zz", 18, "aa", 18}

②:

person2 := Person{
		name:  	"cc",
		heigth:	12,
	}

③:go 编译器底层 对 person.Name 做了转化 (*person).Name。

person3 := new(Person) //var person *Person = new (Person)
person3.name = "dd" //等于(*person3).nam

④:

person4 := &Person{} //var person *Person = &Person{}
person4.name = "ww"

3.2.3 结构体和切片组合

var persons []Person
//第一种添加方式
persons = append(persons, person)
//第二种添加方式
persons = append(persons, Person{
	name: "123",
})
//第三种添加第一种添加方式方式
persons2 := []Person{
	{name: "123", age: 12},
	{age: 11},
}
fmt.Println(persons2)

3.2.4 结构体和函数

package main
import "fmt"

type Person struct {
	name string
	age int
}

//第一种方法,值传递
func (p Person)	print() {
	p.age = 20
	fmt.Printf("name:%s,age:%d\r\n", p.name, p.age)
}

//第二种方法,引用传递
func (p *Person) print2() {
	p.age = 20
	fmt.Printf("name:%s,age:%d\r\n", p.name, p.age)
}
func main() {
	p :=Person{
		name: "文件箱",
		age: 	100,
	}
	p1 :=&Person{
		name: "文件箱",
		age:	100,
	}
	p.print()
	fmt.Println(p.name, p.age)
	fmt.Println("-----------")
	p1.print2()
	fmt.Println(p1.name, p1.age)
}

3.2.5 嵌套匿名结构体

package main
import "fmt"

type person struct {
	name string
	age  int
}
//嵌套
//第一种定义
type student struct {
	p person
	fen  int
}

//第二种定义 匿名结构体
type student2 struct {
	person
	fen int
	name string
}
func main() {
	s :=student{
		person{
			"name",
			18,
		}, 90,
	}
	fmt.Println(s)
	fmt.Println(s.p.name)

	s1 :=student2{
		person{
			"name",12,
		},10,"name2",
	}
	s1.name = "bobby"
	fmt.Println(s1)
	fmt.Println(s1.age)
	fmt.Println(s1.name)
}

3.3 方法

3.3.1 介绍

在某些情况下,我们要需要声明(定义)方法。比如 Person 结构体:除了有一些字段外( 年龄,姓名..),Person 结构体还有一些行为比如:可以说话、跑步..,通过学习,还可以做算术题。这时就要用方法才能完成。

方法的调用和传参机制和函数基本一样,不一样的地方是方法调用时,会将调用方法的变量,当做实参也传递给方法


type A struct {
    Num int
}
func (a A) test() {
    fmt.Println(a.Num)
}

① func (a A) test() {}        表示 A 结构体有一方法,方法名为 test
② (a A) 体现 test 方法是和 A 类型绑定的

3.3.2 方法的定义

func (recevier type) methodName(参数列表) (返回值列表) {

        方法体        

        return 返回值

}

例1:给 Person 结构体添加 speak 方法,输出xxx 是一个好人

func (p Person) speak() {
	fmt.Println(p.name, "是个好人")
}

例2:给 Person 结构体添加 getSum 方法,可以计算两个数的和,并返回结果

func (p Person) getSum(n1,n2 int) int {
	return n1 + n2
}

3.3.3 方法的调用

var p Person
p.speak()
res := p.getSum(10, 20)

3.4 type

type 的用法

        1、给其他类型起别名

        2、定义一个新类型

        3、定义一个结构体

        4、定义接口

package main
import (
	"fmt"
	"strconv"
)

type myint int
func (mi myint) string() string { return strconv.Itoa(int(mi))}


func main() {
	//给类型起别名
	type myint2 = int
	var a int = 10
	var b myint2 = 10
	fmt.Println(a + b)

	//定义一个新类型
	//type myint int
	var c myint = 12
	var d int = 10
	//fmt.Println(c + d) //myint 为一个新的类型不能和int类型进行相加
	fmt.Println(int(c) + d)
	fmt.Printf("%T\r\n", c)
	fmt.Println(c.string())
}

四、 指针

4.1 指针

① 指针存的是一个地址        比如:var ptr *int, 使用*ptr 获取 ptr 指向的值

②值类型和引用类型:

        值类型内存通常在栈中分配        引用类型内存通常在堆上分配,当没有任何变量引用这个地址时,该地址对应的数据空间就成为一个垃圾,由 GC 来回收

     1) 值类型:基本数据类型 int 系列, float 系列, bool, string 、数组和结构体 struct

     2) 引用类型:指针、slice 切片、map、管道 chan、interface 等都是引用类型

③ 指针的两个不同 :

        1.可以做常量来修改,进行了优化

        2.指针是阉割版          在unsafe包中可以使用

④ 指针的初始化(必须):

        初始化两个关键字,map、channel、slice初始化推荐使用make方法

        指针初始化推荐使用new函数,指针要初始化否则会出现nil pointer

        map必须初始化

⑤swap、函数和指针区别:

        swap改的是篮子里的值

        函数传递的本质是 复制一份副本

        指针传递的本质是 地址的传递

package main
import "fmt"

type person struct {
	name string
	age int
} //struct可以不初始化

//指针值的交换
func swap(a, b *int) {
	p := *a
	*a = *b
	*b = p
}
func main() {
	p := &person{
		"wjx", 12,
	}

	//指针的定义
	p.name = "wyh"
	(*p).age = 18 //指针的取值 不能用C语言的p->age
	fmt.Println(p)

	//var pi *person 不行 必须要初始化
	ps := &person{} //第一种初始化
	var emptyPerson person
	pi := &emptyPerson //第二种初始化
	var pp = new(person) //第三种初始化
	fmt.Println(ps.name)
	fmt.Println(pi.name)
	fmt.Println(pp.name)
	a, b := 1, 2
	swap(&a, &b) //不行
	fmt.Println(a, b)
}

4.2 nil

package main
import "fmt"

type Person struct {}

func main() {
	var m1 = map[string]string{}
	m2 := make(map[string]string) //初值empty 不会报错
	if m1 == nil {
		fmt.Println("m1 is nil")
	}
	if m2 == nil {
		fmt.Println("m2 is nil")
	}
	m1["name"] = "boby"
	m2["name"] = "boby1"
	for key, value := range m1 {
		fmt.Println(key, value)
	}
	for key, value := range m2 {
		fmt.Println(key, value)
	}
}

五、 接口

接口是一种特殊的类型

① 接口里的所有方法都没有方法体,即接口的方法都是没有实现的方法。接口体现了程序设计的多态高内聚低偶合的思想。
② Golang 中的接口,不需要显式的实现。只要一个变量,含有接口类型中的所有方法,那么这个变量就实现这个接口。因此,Golang 中没有 implement 这样的关键字
③ Golang 接口中不能有任何变量
④ interface 类型默认是一个指针(引用类型),如果没有对 interface 初始化就使用,那么会输出 nil

5.1 基本语法

interface 类型可以定义一组方法,但是这些不需要实现。并且 interface 不能包含任何变量。到某个自定义类型(比如结构体 Phone)要使用的时候,在根据具体情况把这些方法写出来(实现)。

5.1.1 接口的定义

type 接口名 interface {
        method1(参数列表)(返回值列表)
        method1(参数列表)(返回值列表)
}

5.1.2 接口的实现

func(t自定义类型)method1(参数列表)返回值列表{
        //方法实现
}
func(t自定义类型)method2(参数列表)返回值列表{
        //方法实现
}

实现接口方法:(结构体/自定义类型)
type 自定义类型 struct { }

5.1.3 接口案例

package main
import "fmt"

type animal interface {
	move()
	eat(string)
}

type  cat struct {
	name string
	feet int8
}

func (c cat) move() {
	fmt.Println("走猫步")
}

func (c cat) eat(food string) {
	fmt.Printf("吃%v牌猫粮\n", food)
}

func main(){
	var a1 animal //interface就是一种特殊类型
	bc := cat{
		name: "波斯猫",
		feet: 4,
	}
	a1 = bc //bc要等于a1 就需要满足a1的类型
	a1.eat("超人") //调用eat
	fmt.Println(a1)
}

5.2 指针为接受者

一般都使用指针接受者,而不是值接收者。下例为伪代码,

func (c *cat) move() {
	fmt.Println("走猫步")
}

func (c *cat) eat(food string) {
	fmt.Printf("吃%v牌猫粮\n", food)
}

func main(){
	var a1 animal //interface就是一种特殊类型
	//bc := cat{
	//	name: "波斯猫",
	//	feet: 4,
	//}
	bc := cat {"波斯猫", 4}
	a1 = &bc //bc要等于a1 就需要满足a1的类型
	a1.eat("超人") //调用eat
	fmt.Println(a1)
}

5.3 接口嵌套和多个接口实现

package main
import (
	"fmt"
)

//接口还可以嵌套
//一个结构体可以实现多个接口

type animal interface {
	mover
	eater
}

type mover interface {
	move()
}
type eater interface {
	eat(string)
}

type cat struct {
	name string
	feet int8
}

func (c *cat) move() {
	fmt.Println("走猫步")
}
func (c *cat) eat(food string) {
	fmt.Printf("吃%v牌猫粮\n", food)
}

func main() {
	var a1 animal
	c1 := cat{"波斯猫", 4}
	a1 = &c1
	a1.eat("超人")
	fmt.Println(a1)
}

5.4 空接口

5.4.1 作为函数的参数

func show (a interface{}) (){
	fmt.Printf("type:%T, value:%v\n", a, a)
}

5.4.2 作为value的值

func main() {
	var m1 map[string]interface{}
	m1 = make(map[string]interface{}, 16)
	m1["name"] = "cxk"
	m1["age"] = 38

5.4.3 空接口类型断言

想知道空接口中接受的值是什么

func assign(a interface{}) {
	fmt.Printf("%T\n", a)
	switch t := a.(type) {
	case string:
		fmt.Println("是一个字符串:", t)
	case int:
		fmt.Println("是一个int:", t)
	case int64:
		fmt.Println("是一个int64:", t)
	case bool:
		fmt.Println("是一个布尔类型:", t)
	default:
		panic("not supported type")
	}
}
func add (a, b interface{}) interface{} {
	switch a.(type) {
	case string:
		as, _ := a.(string)
		bs, _ := b.(string)
		return as + bs
	case int:
		ai, _ :=a.(int)
		bi, _ :=b.(int)
		return ai + bi
	case float64:
		af, _ :=a.(float64)
		bf, _ :=b.(float64)
		return af + bf
	default:
		panic("not supported type")
	}
}

  • 11
    点赞
  • 10
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值