主要继续探索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))的过程中我们就可以给代码一个明确的转化方式,提升效率