结构体+接口
结构体
-
-
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

浙公网安备 33010602011771号