3.0.自定义类型
Go语言中使用type关键字来定义自定义类型,下面mylnt就是一种新的类型,它具有int的特性:
type myInt int func main() { var a myInt = 10 fmt.Printf("%v %T", a, a) //10 main.myInt 类型就是myInt类型 }
类型别名,TypeAlias只是Type的别名,本质上是同一个类型。就像一个人有大名、小名、英文名,但这些名字都指的是他本人:
type TypeAlias = Type //rune 和 byte 就是类型别名,他们的底层代码如下 type byte = uint8 type rune = int32
3.1.结构体和方法
结构体是一系列相同类型或不同类型的数据构成的集合表示一项记录,定义:
package main import "fmt" func main() { type treeNode struct { // 一:type name struct创建 value int left, right *treeNode //指针类型 } //用“.” 来访问和定义成员 var root treeNode //root是结构体实例 root.value = 3 fmt.Println(root) //{3 <nil> <nil>} root.left = &treeNode{} fmt.Println(root) //{3 0x1104c0f0 0x1104c100} var roo2 = new(treeNode) // 二:new关键字实例化,得到的是结构体的地址 roo2.value = 3 fmt.Printf("%#v", roo2) //&main.treeNode{value:3, left:(*main.treeNode)(nil), right:(*main.treeNode)(nil)} var roo3 = &treeNode{value:3} // 三:指针创建 fmt.Printf("%#v", roo2) //和二一样 }
结构体方法定义:
package main import "fmt" type treeNode struct { value int } // 定义方法 还能给自定义类型定义方法 //func (接收者变量 接收者类型) 方法名(参数列表)(返回参数) { // 函数体 //} func (node treeNode) setValue(value int) { node.value = value } //由于结构体是值类型,修改实例的时候需要传入指针 func (node *treeNode) setValue(value int) { node.value = value } func main() { var root treeNode root = treeNode{value: 3} root.print() //3 root.setValue(6) root.print() //6 }
结构体允许其成员字段在声明时没有字段名而只有类型,这种没有名字的字段就被称为匿名字段:
type Person struct { string int } func main() { // 结构体的匿名字段 var person = Person{ "张三", 18 } }
结构体字段是指针、slice、和 map 的时初值都是nil,即还没分配空间,使用这些字段,需要先用make函数:
type Person struct { name string age int hobby []string mapValue map[string]string address Address // 结构体嵌套结构体 } type Address struct { name string phone string city string } func main() { var person = Person{} person.hobby = make([]string, 4, 4) person.hobby[0] = "睡觉" person.mapValue = make(map[string]string) person.mapValue["address"] = "北京" // 加入#打印完整信息 fmt.Printf("%#v", person) }
3.2.包和封装
标识符(包括常量、变量、类型、函数名、结构字段等)以大写字母开头,那这种对象就可以被外部包所使用(客户端程序需先导入这个包),这被称为导出(像面向对象语言中的public);标识符如果以小写字母开头,则对包外是不可见的,但是他们在整个包的内部是可见并且可用的(像面向对象语言中的protected)。为结构定义的方法必须放在同一包内
3.3.接口
Go语言的接口把所有的具有共性的方法定义在一起,其他类型只要实现了这些方法就是实现了这个接口,接口中方法只能有名、参数、返回值:
// 定义一个Usber接口 type Usber interface { start() stop() } // 使用结构体来实现 接口 type Phone struct { Name string } // 手机要实现Usber接口的话,必须实现usb接口的所有方法 func (p Phone) start() { fmt.Println(p.Name, "启动") } func (p Phone) stop() { fmt.Println(p.Name, "关闭") } type Camera struct { Name string } // 相机要实现Usber接口的话,也须实现所有方法 func (p Camera) start() { fmt.Println(p.Name, "启动") } func (p Camera) stop() { fmt.Println(p.Name, "关闭") } // 电脑 接收结构体让其工作 type Computer struct { } // 接收一个实现了Usber接口的 结构体 func (computer Computer) Startup(usb Usber) { usb.start() } // 关闭 func (computer Computer) Shutdown (usb Usber) { usb.stop() } func main() { var camera = Camera{ "佳能", } var phone = Phone{ "苹果", } var computer = Computer{} computer.Startup(camera) computer.Startup(phone) computer.Shutdown(camera) computer.Shutdown(phone) } //佳能 启动 //苹果 启动 //佳能 关闭 //苹果 关闭
空接口没有任何约束,任何类型变量都可以实现,所以空接口可以表示任意数据类型:
var a interface{} // 当做类型来使用,可以表任意类型 a = 20 a = "hello" a = true // 空接口作为函数参数 func show(a interface{}) { fmt.println(a) } // 使用空接口实现可以保存任意值的字典 var studentInfo = make(map[string]interface{}) studentInfo["userName"] = "张三" studentInfo["age"] = 15 studentInfo["isWork"] = true // 定义一个空接口类型的切片 var slice = make([]interface{}, 4, 4) slice[0] = "张三" slice[1] = 1 slice[2] = true
一个接口的值是由一个类型和类型值两部分组成的。分别为接口的动态类型和动态值。如果我们想要判断空接口中值的类型,那么可以使用类型断言:
x.(T) // x表示类型为interface{}的变量 t表示断言x可能的类型 返回两个参数,一是x转化为T类型后的值,二是一个布尔值 true表示正确 var a interface{} a = "132" value, isString := a.(string) if isString { fmt.Println("是String类型, 值为:", value) } else { fmt.Println("断言失败") } func Print2(x interface{}) { switch x.(type) { // 类型.(type) 只能结合switch语句使用 case int: fmt.Println("int类型") case string: fmt.Println("string类型") case bool: fmt.Println("bool类型") default: fmt.Println("其它类型") } } Print2(a)
接口嵌套接口:
type Animal1 interface { SetName(string) } type Animal2 interface { GetName()string } type Animal interface { Animal1 // 接口嵌套 Animal2 } type Dog struct { Name string } func (d *Dog) SetName(name string) { d.Name = name } func (d Dog)GetName()string { return d.Name } func main() { var dog = &Dog{ // 方法接受的是指针 所以使用地址 "小黑", } // 同时实现两个接口 var d Animal = dog var d2 Animal2 = dog d.SetName("小鸡") fmt.Println(d.GetName()) }
空接口表任意类型时无法直接通过索引获取切片,结构体等一些数据中的属性或内容,这时需要使用类型断言:
var userInfo = make(map[string]interface{}) userInfo["userName"] = "zhangsan" userInfo["age"] = 10 userInfo["hobby"] = []string{"吃饭", "睡觉"} fmt.Println(userInfo["userName"]) fmt.Println(userInfo["age"]) fmt.Println(userInfo["hobby"]) // fmt.Println(userInfo["hobby"][0]) 这样不行 // 这时我们可以使用类型断言 hobbyValue,ok := userInfo["hobby"].([]string) if ok { fmt.Println(hobbyValue[0]) }