后端go笔记2: 基本语法(cont.) | 青训营

140 阅读3分钟

主要继续探索go的基本语法,包括struct,面向对象特征,空接口,和reflect机制 参考材料:青训营课件,刘丹冰课件(作为补充)

struct

跟c,c++的struct很像,我们可以通过struct自定义定一个新的type name: 这可以是一个复合的结构体,也可以是为一长串(比如c里的iterator)

type myint int //myint 是int的别名

type Book struct{
    title string
    auth string
}

func main(){
    var book1 Book
    book1.title = "Go"
    book1.auth = "me"
}

面向对象特征

封装

struct里var的命名方式:大写开头 -> public;小写开头 -> private

type Book struct{
    Title string
    Auth string
    genre string //genre is private
}

想要给一个struct绑定func,需要用ptr; 比如func (this *Book) Show(){

继承

不像python那样用()继承,而是在child class的第一行标明mother class以表继承:

type ClassicBook struct{
    Book //继承Book类的方法
    
    year int
}

如果子类和父类有同名的func不用virtual,子类直接重新定义就可以覆盖父类的定义

go的子类有两个定义方法,跟以往看到的继承形式差不多:

c1 := ClassicBook(Book{"A","me","Fantasy"},2004}

var c2 ClassicBook
c2.Title = 
c2.Auth = 
c2.genre = 

多态

接口 := 定义class或object应该具备的method signature(不包含具体实现代码,就像.h file那样);有点像大型virtual

GO用interface关键词(ptr)来实现接口,但classes必须跟interface里的func保持一致,要不然ptr指不到具体的class

type AnimalIF interface{ //父类的ptr
    Sleep()
}

type Cat struct{
}
func (this *Cat) Sleep(){
    fmt.Println("Cat is sleeping.")
}
type Dog struct{
}
func (this *Cat) Sleep(){
    fmt.Println("Dog is sleeping.")
}

func main(){
    var animal AnimalIF //接口类型,父类ptr
    animal = &Cat()
    animal.Sleep() //调用的是cat的sleep
    
    anmimal = &Dog()
    animal.Sleep()
}

空接口interface{}

以空接口为parameter可以放入任何dtype的变量,因此如果需要考虑到有各种类型的var的话是个很好的parameter

//省略package main, import
func any(arg interface{}){
    fmt.Print("This is ")
    fmt.Print(arg)
}
type Person struct{
    name string
}
func main(){
    p := Person("Anna")
    any(p) //会打印This is Anna
    any(3.14)
    any("aaa")
}

interface{}的断言机制:利用arg.(<dtype>)判断arg的dtype,我们可以用这个机制做出不同dtype的应对法

以下是个简单的判断arg是不是str的例子:

func any(arg interface{}){
    fmt.Print("This is ")
    fmt.Print(arg)
    
    value, ok := arg.(string)
    if !ok {
        fmt.Println("arg not str")
        fmt.Printf("arg is %T dtype\n",value)
    } else {
        fmt.Println("arg is str")
    }
}

reflect机制

go语言有内置的,包含(dtype, value)的pair结构;这个结构是永久携带传递的(如果其他变量引用,就会复制原变量的这个pair)

reflect机制就是反射出var的dtype(通过reflect.TypeOf)和value(通过reflect.ValueOf),而断言机制靠的就是这个永久携带传递的pair来判断的dtype和值的

用reflect和断言就可以根据不同dtype编写代码/创建不同dtype也通用的代码

reflect有很多用途,可以获取arg中struct的单个field或者arg中的func:

//需要"reflect"包
//假如我有个struct,里面有>=2个field

func DoFiledAndMethod (input interface{}){
    inputType := reflect.TypeOf(input) //获取input dtype
    inputValue := reflect.ValueOf(input) //获取值
    
    //获取单个field;inputType.NumField()是field 的数量
    allElem := inputType.Elem()
    for i := 0; i < inputType.NumField(); i++{
        field := inputType.Field(i) //获取单个field type
        value := inputType.Field(i).Interface() //Interface()获取value
    }
    
    for i := 0; i < inputType.NumMethod(); i++{
        m := inputType.Method(i) //获取单个method
        fmt.Println("%s: %v\n",m.name, m.Type) //method struct包含name和type
    }

结构体标签

绑定标签:便于其他包(比如json)判断怎么用这个field; 可以通过Field.Tag.Get("")来获取

一般源代码就有标签,但如果自己做api可能就需要手动加label

type resume struct{
    Name string `info:"name" doc:"我的名字"`
    Sex string `info:"sex"`
}
func findTag(str interface{}) {
	t := reflect.TypeOf(str).Elem()

	for i := 0; i < t.NumField(); i++ {
		taginfo := t.Field(i).Tag.Get("info")
		tagdoc := t.Field(i).Tag.Get("doc")
		fmt.Println("info: ", taginfo, " doc: ", tagdoc)
	}
}

ex. 利用struct标签生成ideal json

//需要"encoding/json"包
type Movie struct {
	Title  string   `json:"title"` //对应json的格式
	Year   int      `json:"year"`
	Price  int      `json:"rmb"`
	Actors []string `json:"actors"`
}

有了结构体标签,转化到json(jsonStr, err := json.Marshal(movie))和从json转回来(err = json.Unmarshal(jsonStr, &myMovie))的过程中我们就可以给代码一个明确的转化方式,提升效率