结构体+接口

结构体

结构体struct

  • 用来定义复杂的数据结构

  • 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

我们可以为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)
	}
}

接口的实现

  • Golang中的接口,不需要显示的实现,只要一个变量,含有接口类型中的所有方法,那么这个变量就是实现了这个接口。因此,golang中没有implement类似的关键字

  • 相反,如果一个变量只含有一个接口的部分方法,那么这个变量没有实现这个接口

  • 如果一个变量含有了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 

 

posted @ 2019-05-23 22:39  Jin同学  阅读(598)  评论(0)    收藏  举报