一、interface简介
interface(接口)是golang最重要的特性之一,Interface类型可以定义一组方法,但是这些不需要实现。并且interface不能包含任何变量。
简单的说:
- interface是方法的集合
- interface是一种类型,并且是指针类型
- interface的更重要的作用在于多态实现
interface定义
interface使用
- 接口的使用不仅仅针对结构体,自定义类型、变量等等都可以实现接口。
- 如果一个接口没有任何方法,我们称为空接口,由于空接口没有方法,所以任何类型都实现了空接口。
- 要实现一个接口,必须实现该接口里面的所有方法。
<span style="color: #008000;">//<span style="color: #008000;">定义接口
type Skills <span style="color: #0000ff;">interface<span style="color: #000000;"> {
Running()
Getname() <span style="color: #0000ff;">string<span style="color: #000000;">
}
type Student <span style="color: #0000ff;">struct<span style="color: #000000;"> {
Name <span style="color: #0000ff;">string<span style="color: #000000;">
Age <span style="color: #0000ff;">int<span style="color: #000000;">
}
<span style="color: #008000;">//<span style="color: #008000;"> 实现接口
func (p Student) Getname() <span style="color: #0000ff;">string{ <span style="color: #008000;">//<span style="color: #008000;">实现Getname方法
<span style="color: #000000;"> fmt.Println(p.Name )
<span style="color: #0000ff;">return<span style="color: #000000;"> p.Name
}
func (p Student) Running() { <span style="color: #008000;">//<span style="color: #008000;"> 实现 Running方法
fmt.Printf(<span style="color: #800000;">"<span style="color: #800000;">%s running<span style="color: #800000;">"<span style="color: #000000;">,p.Name)
}
func main() {
<span style="color: #0000ff;">var<span style="color: #000000;"> skill Skills
<span style="color: #0000ff;">var<span style="color: #000000;"> stu1 Student
stu1.Name = <span style="color: #800000;">"<span style="color: #800000;">wd<span style="color: #800000;">"<span style="color: #000000;">
stu1.Age = <span style="color: #800080;">22<span style="color: #000000;">
skill =<span style="color: #000000;"> stu1
skill.Running() <span style="color: #008000;">//<span style="color: #008000;">调用接口
<span style="color: #000000;">}
<span style="color: #008000;">//<span style="color: #008000;">wd running
多态
上面提到了,go语言中interface是实现多态的一种形式,所谓多态,就是一种事物的多种形态,与python中类的多态是一致的。
同一个interface,不同的类型实现,都可以进行调用,它们都按照统一接口进行操作。
在上面的示例中,我们增加一个Teacher结构体,同样实现接口进行说明:
type Skills <span style="color: #0000ff;">interface<span style="color: #000000;"> {
Running()
Getname() <span style="color: #0000ff;">string<span style="color: #000000;">
}
type Student <span style="color: #0000ff;">struct<span style="color: #000000;"> {
Name <span style="color: #0000ff;">string<span style="color: #000000;">
Age <span style="color: #0000ff;">int<span style="color: #000000;">
}
type Teacher <span style="color: #0000ff;">struct<span style="color: #000000;"> {
Name <span style="color: #0000ff;">string<span style="color: #000000;">
Salary <span style="color: #0000ff;">int<span style="color: #000000;">
}
func (p Student) Getname() <span style="color: #0000ff;">string{ <span style="color: #008000;">//<span style="color: #008000;">实现Getname方法
<span style="color: #000000;"> fmt.Println(p.Name )
<span style="color: #0000ff;">return<span style="color: #000000;"> p.Name
}
func (p Student) Running() { <span style="color: #008000;">//<span style="color: #008000;"> 实现 Running方法
fmt.Printf(<span style="color: #800000;">"<span style="color: #800000;">%s running<span style="color: #800000;">"<span style="color: #000000;">,p.Name)
}
func (p Teacher) Getname() <span style="color: #0000ff;">string{ <span style="color: #008000;">//<span style="color: #008000;">实现Getname方法
<span style="color: #000000;"> fmt.Println(p.Name )
<span style="color: #0000ff;">return<span style="color: #000000;"> p.Name
}
func (p Teacher) Running() { <span style="color: #008000;">//<span style="color: #008000;"> 实现 Running方法
fmt.Printf(<span style="color: #800000;">"<span style="color: #800000;">\n%s running<span style="color: #800000;">"<span style="color: #000000;">,p.Name)
}
func main() {
<span style="color: #0000ff;">var<span style="color: #000000;"> skill Skills
<span style="color: #0000ff;">var<span style="color: #000000;"> stu1 Student
<span style="color: #0000ff;">var<span style="color: #000000;"> t1 Teacher
t1.Name = <span style="color: #800000;">"<span style="color: #800000;">wang<span style="color: #800000;">"<span style="color: #000000;">
stu1.Name = <span style="color: #800000;">"<span style="color: #800000;">wd<span style="color: #800000;">"<span style="color: #000000;">
stu1.Age = <span style="color: #800080;">22<span style="color: #000000;">
skill =<span style="color: #000000;"> stu1
skill.Running()
skill =<span style="color: #000000;"> t1
t1.Running()
}
<span style="color: #008000;">//<span style="color: #008000;">wd running
<span style="color: #008000;">//<span style="color: #008000;">wang running
接口嵌套
go语言中的接口可以嵌套,可以理解我继承,子接口拥有父接口的所有方法,并且想要使用该子接口的话,必须将父接口和子接口的所有方法都实现。
type Test
<span style="color: #0000ff;">interface<span style="color: #000000;"> {sleeping()
Skills <span style="color: #008000;">//<span style="color: #008000;">继承Skills
}
类型转换
由于接口是一般类型,当我们使用接口时候可能不知道它是那个类型实现的,基本数据类型我们有对应的方法进行类型转换,当然接口类型也有类型转换。
当然我们也可以用这个方式来进行类型的判断。
转换方式:
y,ok := x.(<span style="color: #0000ff;">int) <span style="color: #008000;">//<span style="color: #008000;">将interface 转为int,ok可省略 但是省略以后转换失败会报错,true转换成功,false转换失败,并采用默认值
示例:
func main() {
<span style="color: #0000ff;">var x <span style="color: #0000ff;">interface<span style="color: #000000;">{}
s :</span>= <span style="color: #800000;">"</span><span style="color: #800000;">WD</span><span style="color: #800000;">"</span><span style="color: #000000;">
x </span>=<span style="color: #000000;"> s
y,ok :</span>= x.(<span style="color: #0000ff;">int</span><span style="color: #000000;">)
z,ok1 :</span>= x.(<span style="color: #0000ff;">string</span><span style="color: #000000;">)
fmt.Println(y,ok)
fmt.Println(z,ok1)
}
<span style="color: #008000;">//<span style="color: #008000;">0 false
<span style="color: #008000;">//<span style="color: #008000;">WD true
判断类型示例:
type Student <span style="color: #0000ff;">struct<span style="color: #000000;"> {
Name <span style="color: #0000ff;">string<span style="color: #000000;">
}
func TestType(items ...<span style="color: #0000ff;">interface<span style="color: #000000;">{}) {
<span style="color: #0000ff;">for k,v :=<span style="color: #000000;"> range items {
<span style="color: #0000ff;">switch<span style="color: #000000;"> v.(type) {
<span style="color: #0000ff;">case <span style="color: #0000ff;">string<span style="color: #000000;">:
fmt.Printf(<span style="color: #800000;">"<span style="color: #800000;">type is string,%d[%v]\n<span style="color: #800000;">"<span style="color: #000000;">,k,v)
<span style="color: #0000ff;">case <span style="color: #0000ff;">bool<span style="color: #000000;">:
fmt.Printf(<span style="color: #800000;">"<span style="color: #800000;">type is bool,v)
<span style="color: #0000ff;">case <span style="color: #0000ff;">int<span style="color: #000000;">:
fmt.Printf(<span style="color: #800000;">"<span style="color: #800000;">type is int,v)
<span style="color: #0000ff;">case<span style="color: #000000;"> float32,float64:
fmt.Printf(<span style="color: #800000;">"<span style="color: #800000;">type is float,v)
<span style="color: #0000ff;">case<span style="color: #000000;"> Student:
fmt.Printf(<span style="color: #800000;">"<span style="color: #800000;">type is Student,v)
<span style="color: #0000ff;">case *<span style="color: #000000;">Student:
fmt.Printf(<span style="color: #800000;">"<span style="color: #800000;">type is Student,%d[%p]\n<span style="color: #800000;">"<span style="color: #000000;">,v)
}
}
}
func main() {
<span style="color: #0000ff;">var<span style="color: #000000;"> stu Student
TestType(<span style="color: #800000;">"<span style="color: #800000;">WD<span style="color: #800000;">",<span style="color: #800080;">100,stu,<span style="color: #800080;">3.3<span style="color: #000000;">)
}
<span style="color: #008000;">//<span style="color: #008000;">type is string,0[WD]
<span style="color: #008000;">//<span style="color: #008000;">type is int,1[100]
<span style="color: #008000;">//<span style="color: #008000;">type is Student,2[{}]
<span style="color: #008000;">//<span style="color: #008000;">type is float,3[3.3]
二、反射reflect
反射是程序执行时检查其所拥有的结构。尤其是类型的一种能力。这是元编程的一种形式。它同一时候也是造成混淆的重要来源。
每一个语言的反射模型都不同(同一时候很多语言根本不支持反射,python通过hasattr方法实现)
go语言中的反射通过refect包实现,reflect包实现了运行时反射,允许程序操作任意类型的对象。
在介绍反射之前先说明下reflect包中的两个数据类Type和Value。
Type
Type:Type类型用来表示一个go类型。
不是所有go类型的Type值都能使用所有方法。请参见每个方法的文档获取使用限制。在调用有分类限定的方法时,应先使用Kind方法获知类型的分类。调用该分类不支持的方法会导致运行时的panic。
示例:
<span style="color: #800000;">"<span style="color: #800000;">reflect<span style="color: #800000;">"
<span style="color: #800000;">"<span style="color: #800000;">fmt<span style="color: #800000;">"<span style="color: #000000;">
)
func main() {
str := <span style="color: #800000;">"<span style="color: #800000;">wd<span style="color: #800000;">"<span style="color: #000000;">
res_type :=<span style="color: #000000;"> reflect.TypeOf(str)
fmt.Println(res_type) <span style="color: #008000;">//<span style="color: #008000;">string
}
reflect.Type中方法
通用方法:
(t *rtype) () reflect.Type 获取元素类型、获取指针所指对象类型,获取接口的动态类型
示例:
<span style="color: #800000;">"<span style="color: #800000;">fmt<span style="color: #800000;">"
<span style="color: #800000;">"<span style="color: #800000;">reflect<span style="color: #800000;">"<span style="color: #000000;">
)
type Skills <span style="color: #0000ff;">interface<span style="color: #000000;"> {
reading()
running()
}
type Student <span style="color: #0000ff;">struct<span style="color: #000000;"> {
Name <span style="color: #0000ff;">string<span style="color: #000000;">
Age <span style="color: #0000ff;">int<span style="color: #000000;">
}
func (self Student) runing(){
fmt.Printf(<span style="color: #800000;">"<span style="color: #800000;">%s is running\n<span style="color: #800000;">"<span style="color: #000000;">,self.Name)
}
func (self Student) reading(){
fmt.Printf(<span style="color: #800000;">"<span style="color: #800000;">%s is reading\n<span style="color: #800000;">"<span style="color: #000000;">,self.Name)
}
func main() {
stu1 := Student{Name:<span style="color: #800000;">"<span style="color: #800000;">wd<span style="color: #800000;">",Age:<span style="color: #800080;">22<span style="color: #000000;">}
inf := <span style="color: #0000ff;">new<span style="color: #000000;">(Skills)
stu_type :=<span style="color: #000000;"> reflect.TypeOf(stu1)
inf_type := reflect.TypeOf(inf).Elem() <span style="color: #008000;">//<span style="color: #008000;"> 特别说明,引用类型需要用Elem()获取指针所指的对象类型
fmt.Println(stu_type.String()) <span style="color: #008000;">//<span style="color: #008000;">main.Student
fmt.Println(stu_type.Name()) <span style="color: #008000;">//<span style="color: #008000;">Student
fmt.Println(stu_type.PkgPath()) <span style="color: #008000;">//<span style="color: #008000;">main
fmt.Println(stu_type.Kind()) <span style="color: #008000;">//<span style="color: #008000;">struct
fmt.Println(stu_type.Size()) <span style="color: #008000;">//<span style="color: #008000;">24
fmt.Println(inf_type.NumMethod()) <span style="color: #008000;">//<span style="color: #008000;">2
fmt.Println(inf_type.Method(<span style="color: #800080;">0),inf_type.Method(<span style="color: #800080;">0).Name) <span style="color: #008000;">//<span style="color: #008000;"> {reading main func()
fmt.Println(inf_type.MethodByName(<span style="color: #800000;">"<span style="color: #800000;">reading<span style="color: #800000;">")) <span style="color: #008000;">//<span style="color: #008000;">{reading main func()
<span style="color: #000000;">
}
其他方法:
<span style="color: #008000;">//
<span style="color: #008000;"> 数组<span style="color: #000000;">
func (t *rtype) Len() <span style="color: #0000ff;">int <span style="color: #008000;">//<span style="color: #008000;"> 获取数组的元素个数
<span style="color: #008000;">//<span style="color: #008000;"> 映射
<span style="color: #000000;">
func (t *rtype) Key() reflect.Type <span style="color: #008000;">//<span style="color: #008000;"> 获取映射的键类型
<span style="color: #008000;">//<span style="color: #008000;"> 通道
<span style="color: #000000;">
func (t *rtype) ChanDir() reflect.ChanDir <span style="color: #008000;">//<span style="color: #008000;"> 获取通道的方向
<span style="color: #008000;">//<span style="color: #008000;"> 结构体
<span style="color: #000000;">
func (t rtype) NumField() <span style="color: #0000ff;">int <span style="color: #008000;">//<span style="color: #008000;"> 获取字段数量
<span style="color: #000000;">
func (t rtype) Field(<span style="color: #0000ff;">int) reflect.StructField <span style="color: #008000;">//<span style="color: #008000;"> 根据索引获取字段
<span style="color: #000000;">
func (t rtype) FieldByName(<span style="color: #0000ff;">string) (reflect.StructField,<span style="color: #0000ff;">bool) <span style="color: #008000;">//<span style="color: #008000;"> 根据名称获取字段
<span style="color: #000000;">
func (t rtype) FieldByNameFunc(match func(<span style="color: #0000ff;">string) <span style="color: #0000ff;">bool) (reflect.StructField,<span style="color: #0000ff;">bool) <span style="color: #008000;">//<span style="color: #008000;"> 根据指定的匹配函数 math 获取字段
<span style="color: #000000;">
func (t *rtype) FieldByIndex(index []<span style="color: #0000ff;">int) reflect.StructField <span style="color: #008000;">//<span style="color: #008000;"> 根据索引链获取嵌套字段
<span style="color: #008000;">//<span style="color: #008000;"> 函数
<span style="color: #000000;">
func (t rtype) NumIn() <span style="color: #0000ff;">int <span style="color: #008000;">//<span style="color: #008000;"> 获取函数的参数数量
<span style="color: #000000;">
func (t rtype) In(<span style="color: #0000ff;">int) reflect.Type <span style="color: #008000;">//<span style="color: #008000;"> 根据索引获取函数的参数信息
<span style="color: #000000;">
func (t rtype) NumOut() <span style="color: #0000ff;">int <span style="color: #008000;">//<span style="color: #008000;"> 获取函数的返回值数量
<span style="color: #000000;">
func (t rtype) Out(<span style="color: #0000ff;">int) reflect.Type <span style="color: #008000;">//<span style="color: #008000;"> 根据索引获取函数的返回值信息
<span style="color: #000000;">
func (t *rtype) IsVariadic() <span style="color: #0000ff;">bool <span style="color: #008000;">//<span style="color: #008000;"> 判断函数是否具有可变参数。
<span style="color: #008000;">//<span style="color: #008000;"> 如果有可变参数,则 t.In(t.NumIn()-1) 将返回一个切片。
示例:
<span style="color: #800000;">"<span style="color: #800000;">fmt<span style="color: #800000;">"
<span style="color: #800000;">"<span style="color: #800000;">reflect<span style="color: #800000;">"<span style="color: #000000;">
)
type Skills <span style="color: #0000ff;">interface<span style="color: #000000;"> {
reading()
running()
}
type Student <span style="color: #0000ff;">struct<span style="color: #000000;"> {
Name <span style="color: #0000ff;">string<span style="color: #000000;">
Age <span style="color: #0000ff;">int<span style="color: #000000;">
}
func (self Student) runing(){
fmt.Printf(<span style="color: #800000;">"<span style="color: #800000;">%s is running\n<span style="color: #800000;">"<span style="color: #000000;">,Age:<span style="color: #800080;">22<span style="color: #000000;">}
stu_type :=<span style="color: #000000;"> reflect.TypeOf(stu1)
fmt.Println(stu_type.NumField()) <span style="color: #008000;">//<span style="color: #008000;">2
fmt.Println(stu_type.Field(<span style="color: #800080;">0)) <span style="color: #008000;">//<span style="color: #008000;">{Name string 0 [0] false}
fmt.Println(stu_type.FieldByName(<span style="color: #800000;">"<span style="color: #800000;">Age<span style="color: #800000;">")) <span style="color: #008000;">//<span style="color: #008000;">{{Age int 16 [1] false} true
}
Value
不是所有go类型值的Value表示都能使用所有方法。请参见每个方法的文档获取使用限制。在调用有分类限定的方法时,应先使用Kind方法获知该值的分类。调用该分类不支持的方法会导致运行时的panic。
示例:
reflect.Value方法
注意:以下所有方法中的v是reflect.Value返回的值。
reflect.Value.Kind():获取变量类别,返回常量