Go语言从入门到精通三——struct和interface

Go中的struct

struct用来自定义复杂数据结构,相当于其他面向对象语言中的Class。

  • struct里面可以包含多个字段(属性)
  • struct类型可以自定义方法,注意和函数的区分:方法有一个接受对象,而函数没有
  • struct类型是值类型
  • struct类型可以嵌套
  • Go语言没有class类型,只有struct类型

struct声明

语法:
type 标识符 struct{
field1 type
field2 type
}
如:

type Student struct{
	Name string 
	Age int
	Score int
}

struct定义的三种形式

var stu Student
var stu *Student = new(Student)
var stu *Student = &Student{}

其中,后两种返回的都是指向结构体的指针,访问形式如下:
stu.Namestu.Agestu.Score或者 (*stu).Name(*stu).Age(*stu).Score

struct的内存布局

struct中的所有字段在内存是连续的,布局如下:
Go语言从入门到精通三——struct和interface_第1张图片

struct工厂模式

golang中的struct没有构造函数,一般可以使用工厂模式来解决这个问题。

package model

import "fmt"

type student struct {
	name string
	age int
}

func NewStudent(name string, age int) *student {
	return &student{
		name: name,
		age:  age,
	}
}

func (s student)Print()  {
	fmt.Println(s.name, s.age)
}
package main

import (
	"golang_learning/model"
	"fmt"
	"strings"
)

func main() {
	s := model.NewStudent("sam", 18)
	s.Print()
	fmt.Println(strings.Repeat("*", 10))
}

struct中的tag

我们可以为struct中的每个字段,写上一个tag。这个tag可以通过反射的机制获取到,最常用的场景就是json序列化和反序列化。

type student struct {
	Name string  "this is name field"
	Age int      "this is age field"
}

还有一种场景是,由于golang中的字段大写才能导出,到与其他语言交互时,字段名多为小写,这个时候也可以使用tag来解决。

type student struct {
	Name string  "name"
	Age int      "age"
}

struct中的匿名字段

type Wheel struct {
	Name string
	Size int
}

type Car struct {
	Wheel
	Comp string
	Date time.Time
	int
}

字段冲突的处理

只要冲突的字段不再同一个级别,就不会panic;可以通过明确指定来访问。

type Wheel struct {
	Name string
	Size int
}

type Car struct {
	Wheel
	Comp string
	Date time.Time
	Size int
}
	w := Wheel{"w", 1}
	c := Car{w, "auto", time.Now(), 20}
	fmt.Println("%v", w)
	fmt.Println("%v", c)
	fmt.Println(c.Size)
	fmt.Println(c.Wheel.Size)
	// 20
	// 1

golang中的方法

Golang中的方法是作用在特定类型的变量上,因此自定义类型,都可以有方法,而不仅仅是struct。如:

package main

import (
	"fmt"
)

type Int int

func (i Int)print()  {
	fmt.Println(i)
}

func main() {
	i := Int(99)
	i.print()
}
// 99

定义:func (recevier type) methodName(参数列表)(返回值列表){}
另外,方法的访问控制,通过大小写控制。

方法和函数的区别:

  • 函数调用: function(variable, 参数列表)
  • 方法:variable.function(参数列表)

指针receiver vs 值receiver

结构体是值类型的,如果需要修改其内部的值,需要使用指针。

继承和组合

模拟继承

如果一个struct嵌套了另一个匿名结构体,那么这个结构可以直接访问匿名结构体的方法,从而实现了继承。

多重继承

如果一个struct嵌套了多个匿名结构体,那么这个结构可以直接访问多个匿名结构体的方法,从而实现了多重继承。

组合

如果一个struct嵌套了另一个有名结构体,那么这个模式就叫组合。

String()方法

如果一个变量实现了String()这个方法,那么fmt.Println默认会调用这个
变量的String()进行输出。

package main

import (
	"fmt"
	"strconv"
)

type student struct{
	name string
	age int
}

func (s student)String() string{
	return "name: " + s.name + ", age: " + strconv.Itoa(s.age)
}
func main() {
	s := student{"lee", 18}
	fmt.Println(s)
	fmt.Printf("%s", s)
}
//  name: lee, age: 18
//  name: lee, age: 18

接口

Interface类型可以定义一组方法,但是这些不需要实现。并且interface不能
包含任何变量。

接口的定义

interface类型默认是一个指针

type example interface{

        Method1(参数列表) 返回值列表
        Method2(参数列表) 返回值列表
        ...
}
var a example
a.Method1()

接口实现

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

多态

一种事物的多种形态,都可以按照统一的接口进行操作

接口嵌套

一个接口可以嵌套在另外的接口,比如:

type ReadWrite interface {
	Read(b int) bool
	Write(b int) bool
}
type Lock interface {
	Lock()
	Unlock()
}
type File interface {
	ReadWrite
	Lock
	Close()
}

类型断言

由于接口是一般类型,不知道其具体类型,如果要转换成具体类型,可以使用下面的方法进行转换:

var t int
var x interface{}
x = t
y, ok := x.(int)   // 转成int
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 int, int64:
			fmt.Printf("param #%d is an int\n", i)
		case nil:
			fmt.Printf("param #%d is nil\n", i)
		case string:
			fmt.Printf("param #%d is a string\n", i)
		default:
			fmt.Printf("param #%d’s type is unknown\n", i)
		}
	}
}

空接口

空接口没有任何方法,所以所有类型都实现了空接口。

var a int
var b interface{}
b = a

判断一个变量是否实现了指定接口

type Stringer interface {
	String() string
}

var v MyStruct
if sv, ok := v.(Stringer); ok {
	fmt.Printf("v implements String():%s\n", sv.String())
}

变量slice和接口slice之间赋值操作

变量slice和接口slice之间赋值操作,应该使用for range

	var a []int
	var b []interface{}
	// b = a   //error!!!
	for index, value := range a{
		b[index] = value
	}
	fmt.Println(b)

你可能感兴趣的:(go)