结构体+接口
结构体
-
-
struct里面可以包含多个字段(属性)
-
struct类型可以定义方法,注意和函数的区别
-
struct类型是值类型
-
struct类型可以嵌套
-
Go语言没有class类型,只有struct类型
type 标识符 struct { field1 type field2 type } // eg type Student struct { Name string Age string Score int }
var stu Student stu.Name = "tony" stu.Age = 18 stu.Score = 20
初始化
结构体的初始化有三种方式
var stu Student var stu *Student = new(Student) var stu *Student = &Student{} var stu = Student{ Name = "hh", Age = 17, }
链表的每个节点包含下一个节点的地址,这样把所有的节点串联起来了,通常把链表中的第一个节点叫做链表头
type Student struct { Name string Next *Student }
type Student struct { Name string Age int Score float32 next *Student } func insertTail(p *Student) { var tail = p for i := 0; i < 10; i++ { var stu = Student{ Name: fmt.Sprint("stu%d", i), Age: rand.Intn(100), Score: rand.Float32() * 100, } tail.next = &stu tail = &stu } } func trans(p *Student) { for p != nil { fmt.Println(*p) p = p.next } } func main() { var head Student head.Name = "hha" head.Age = 18 head.Score = 100 insertTail(&head) trans(&head) }
打印结果是
{stu0 81 94.05091 0xc0000841b0} {stu1 47 43.77142 0xc0000841e0} {stu2 81 68.682304 0xc000084210} {stu3 25 15.651925 0xc000084240} {stu4 56 30.091187 0xc000084270} {stu5 94 81.36399 0xc0000842a0} {stu6 62 38.06572 0xc0000842d0} {stu7 28 46.888985 0xc000084300} {stu8 11 29.310184 0xc000084330} {stu9 37 21.855305 <nil>}
可以看到,从0 - 9
type Student struct { Name string Age int Score float32 next *Student } func insertTail(p *Student) { var head = p for i := 0; i < 10; i++ { var stu = Student{ Name: fmt.Sprint("stu%d", i), Age: rand.Intn(100), Score: rand.Float32() * 100, } stu.next = head head = &stu } trans(head) } func trans(p *Student) { for p != nil { fmt.Println(*p) p = p.next } } func main() { var head Student head.Name = "hha" head.Age = 18 head.Score = 100 insertTail(&head) }
可以看到结果从9-0
{stu9 37 21.855305 0xc000084330} {stu8 11 29.310184 0xc000084300} {stu7 28 46.888985 0xc0000842d0} {stu6 62 38.06572 0xc0000842a0} {stu5 94 81.36399 0xc000084270} {stu4 56 30.091187 0xc000084240} {stu3 25 15.651925 0xc000084210} {stu2 81 68.682304 0xc0000841e0} {stu1 47 43.77142 0xc0000841b0} {stu0 81 94.05091 0xc000084180}
其实尾部插入与头部插入的区别就是将stu插在head后面还是将head插在stu后面的区别
type Student struct { Name string left *Student right *Student }
如果每个节点有两个指针分别指向左子树和右子树,我们把这样的结构叫做二叉树
golang中的strut是没有构造函数的,一般可以使用工厂模式来解决这个问题
package model type Student struct { Name string Age int } func NewStudent(name string, age int) *Student { return &Student{ Name: name, Age: age, } } package main func main() { stu := model.NewStudent("tony", 20) }
我们可以为struct中的每个字段,写一个tag,这个tag可以通过反射的机制获取到,最常用的场景就是json的序列化和反序列化
type Student struct { Name string "this is name field" Age int "this is age field" } // example type Stuent struct { Name string `json:"name"` Age int `json:"age"` } var stu = Student{ Name: "Jim", Age: 12, } val, err := json.Marshal(stu) // val是一个byte数组
结构体中的字段可以没有名字,即匿名字段
type Car struct { name string age int } type Train struct { Car // 匿名字段 Start time.Time int // 匿名字段 } // 使用 func main() { var t Train t.name = "Scarlett" // 直接设置Car里面的字段 t.age = 34 t.int = 200 // 设置int字段 t.Car.name = "Taylor" // 这样也可以 }
Golang中的方法是作用在特定类型的变量上的,因此自定义类型,都可以有方法,而不仅仅是struct
func (receiver type) methodName(参数列表) (返回值列表) {} // 例子 type Student struct { Name string Age int Score int } func (p Student) init(name string, age int, score int) { p.Name = name p.Age = age p.Score = score fmt.Println(p) } // 调用 func main() { var stu *Student = new(Student) stu.init("stu", 10, 200) }
其实不只是结构体能使用方法,其他的类型也能使用
type integer int // 自定义一个integer类型 func (p integer) print { fmt.Println(p) } func main() { var a integer a = 100 a.print() // 打印100 }
接口
Interface类型可以定义一组方法,但是这些不需要实现,并且interface不能包含任何变量
定义
type example interface { Method1(参数列表) 返回值列表 Method2(参数列表) 返回值列表 ... }
实例一:
type Test interface { Print() } type Student struct { name string age int score int } // 只要结构体实现了接口中的全部方法,就相当于实现了接口 func (p *Student) Print() { fmt.Println(p.name) } func main() { var t Test var stu Student = Student{ name: "stu1", age: 20, score: 200, } t = &stu t.Print() }
实例二:
在sort包中,有一个Sort()方法,方法的参数是一个接口,即Sort(Interface interface),因此,如果我们自定义的类型想要实现排序,就只需要实现这个接口,就可以调用这个方法进行排序了
package main import ( "fmt" "math/rand" "sort" ) type Student struct { Name string Id string Age int } type studentArray []Student /* 实现接口 */ func (p studentArray) Len() int { return len(p) } func (p studentArray) Less(i, j int) bool { return p[i].Age > p[j].Age } func (p studentArray) Swap(i, j int) { p[i], p[j] = p[j], p[i] } func main() { var stus studentArray for i := 0; i < 10; i++ { stu := Student{ Name: fmt.Sprint("stu%d", rand.Intn(100)), Id: fmt.Sprint("110%d", rand.Int()), Age: rand.Intn(100), } stus = append(stus, stu) } for _, val := range stus { fmt.Println(val) } fmt.Println("\n\n") // 因为studentArray(即[]Student)已经实现了Sort方法所需要的接口,所以直接传入Student切片即可 sort.Sort(stus) for _, val := range stus { fmt.Println(val) } }
-
-
相反,如果一个变量只含有一个接口的部分方法,那么这个变量没有实现这个接口
-
如果一个变量含有了interface类型的所有方法,那么这个变量就实现了这个接口
type ReadWrite interface { Read(b Buffer) bool Write(b Buffer) bool } type Lock interface { Lock() UnLock() } type File interface { ReadWrite // 嵌套 Lock // 嵌套 Close() } // 这个方法传入的是ReadWrite接口,但是也可以传入嵌套了ReadWrite接口的File接口 func Test(rw ReadWrite) { rw.Read() rw.Write() } func main() { var f File Test(&f) }
var t int var x interface{} x = t // 此时的x是一般类型,是不知道类型的 y := x.(int) // 转成int y, ok := x.(string) // ok可以接收转换的结果 fmt.Printf("%T", y)
demo:判断参数类型
func classifier(items...interface{}) { for i, x := range items { switch x.(type) { case bool: fmt.Printf("param #%d is a bool\n", i) case float64: fmt.Printf("param #%d is a float64\n", i) case nil: fmt.Printf("param #%d is a nil\n", i) default: fmt.Printf("param #%d is type unknow\n", i) } } }
type Reader interface { Read() } type File struct { } func (f File) Read() { fmt.Println(f) } func main() { var f File var b interface{} b = f a, ok := b.(Reader) // 判断f是否实现了Reader接口 fmt.Println(a, ok) }
返回 {} true