Go学习笔记——类型与接口
如果说go语言的其他内容看起来和c/c++语言没什么太大的区别,那么它的接口设计一定会让人大吃一惊,是的,有时它真的让我产生我使用的是一种动态语言的幻觉。
结构类型
type Man struct { name string age int }
声明结构变量及初始化:
var m Man //声明Man变量 m := new(Man) //声明一个Man指针 m := Man{name:"jack",age:12} //声明并初始化 m := Man{"jack",12} //声明并初始化
方法
func (m *Man) Introduce() bool { fmt.Println("name: ",m.name) return true }
调用方法使用了"."符号:
m := Man{"Jack",12} ok := m.Introduce()利用结构的方法,我们甚至还能像ruby一样,为系统中预定类型打个猴子补丁,添加新的方法,只不过手段稍微曲线了一点,比如,下面代码为string类型添加一个chomp方法来去掉字符串最后一个换行符:
package main import ( "fmt" "strings" ) type MyString string func (str MyString) chomp() string { return strings.TrimRight(string(str),"\n") } func main() { str := "Hello world!\n" ms := MyString(str) fmt.Println(ms.chomp()) //输出 Hello world! }
方法代理
package main import "fmt" func main() { n := new(Neo) n.Fly() //输出:The One can fly! } type Neo struct{ TheOne } type TheOne struct{} func (o *TheOne) Fly(){ fmt.Println("The One can fly!") }
在ruby语言中,有许多做这种方法代理的代码,而go语言的嵌入类型的表征和该特性竟是如此神似,嘿嘿
go style duck-type
type Duck interface { run() height() int gaga(word string) }
Duck接口中仅包含了一系列的方法声明。
func DuckRun(d Duck){ d.run() }
那么是不是我们需要去完成多个Duck接口的实现呢?NO。那样岂不是和java一样了。go语言的interface的唯一用处其实是为了满足编译器(唉,多少有点无奈),这样编译器在DuckRun函数入口会检查传递进来的对象是否含有Duck接口所包含的三个方法,如果符合定义,则通过编译。这样不就是鸭子类型吗?仅检查是否包含这些方法,而不管是否是某种类型,所以说go style的鸭子类型是很了不起的。
package main import "fmt" func main() { m := Man{name:"Jack"} m.say() e := new(Earch) SaySth(e) SaySth(&m) //say()方法的接收者是一个指针变量,所以这里要用&取地址 } func SaySth(obj Object){ obj.say() } type Object interface{ say() } type Man struct{ name string } type Earch struct{} func (m *Man) say(){ fmt.Println("Man says: I'm ",m.name) } func (e *Earch) say(){ //do nothing }
空接口及类型
// 定义a为空接口 var a interface{} var i int = 5 s := "Hello world" // a可以存储任意类型的数值 a=i,a=s
我们知道interface的变量里面可以存储任意类型的数值(该类型实现了interface)。那么我们怎么反向知道这个变量 里面实际保存了的是哪个类型的对象呢?可以使用Comma-ok断言
value, ok = element.(T)
这里value就是变 量的值,ok是一个bool类型,element是interface变量,T是断言的类型。
如果element里面确实存储了T类型的数值,那么ok返回true,否则返回false。
此外,关于go语言中类型处理的要求更多时,就需要使用反射。Go语言实现了反射,所谓反射就是动态运行时的状态。我们一般用到的包是reflect包。使用reflect一般分成三步,下面简要的讲解一下:要去反射是一个类型的值(这些值都实现了空interface),首先需 要把它转化成reflect对象(reflect.Type或者reflect.Value,根据不同的情况调用不同的函数)。这两种获取方式如 下:
t := reflect.TypeOf(i) //得到类型的元数据,通过t我们能获取类型定义里面的所有元素 v := reflect.ValueOf(i) //得到实际的值,通过v我们获取存储在里面的值,还可以去改变值
郑重声明:本站内容如果来自互联网及其他传播媒体,其版权均属原媒体及文章作者所有。转载目的在于传递更多信息及用于网络分享,并不代表本站赞同其观点和对其真实性负责,也不构成任何其他建议。