接口详解

// 举例:sort包中的 Sort 函数,如下:
func Sort(data Interface)
Sort sorts data. It makes one call to data.Len to determine n, and O(n*log(n)) calls to data.Less and data.Swap. The sort is not guaranteed to be stable.
(Sort 对 data 进行排序。 它调用一次 data.Len 来决定排序的长度 n,调用 data.Less 和 data.Swap 的开销为 O(n*log(n))。此排序为不稳定排序。) type Interface interface {
// Len is the number of elements in the collection.
Len() int
// Less reports whether the element with
// index i should sort before the element with index j.
Less(i, j int) bool
// Swap swaps the elements with indexes i and j.
Swap(i, j int)
}

举例来扩展 Sort 函数的功能,代码如下:

package main

import (
"fmt"
"sort"
"math/rand"
) type Student struct {
Name string
Id string
Age int
} type StudentSlice []Student // 自定义一个切片类型 StudentSlice,切片中的元素是 Student 类型 func (p StudentSlice) Len() int {
return len(p)
} func (p StudentSlice) Less(i, j int) bool {
return p[i].Name < p[j].Name
} func (p StudentSlice) Swap(i, j int) {
p[i], p[j] = p[j], p[i]
} // StudentSlice 类型完全实现了 sort.Sort() 形参中的 Interface 类型,那么 就可以用 sort.Sort() 函数对 StudentSlice 类型的数据进行排序 func main(){
var stuslice StudentSlice
for i := ; i < ; i++ {
stu := Student{ // 生成 Student 类型的结构体
Name: fmt.Sprintf("stu%d",rand.Intn()),
Id:fmt.Sprintf("110%d",rand.Int()),
Age: rand.Intn(),
}
stuslice = append(stuslice,stu) // Student 类型的结构体添加到 stuslice 切片中
} for _, v := range stuslice {
fmt.Println(v)
} fmt.Println("\n") sort.Sort(stuslice) // 由于stuslice 类型实现了Interface这个接口,那么就能调用 sort.Sort() 对其进行排序 for _, v := range stuslice {
fmt.Println(v)
}
} // 编译后运行结果如下:
[root@NEO project]# go build -o bin/example01_interface_extend go_dev/day06/example01_interface_extend/main
[root@NEO project]# bin/example01_interface_extend
{stu81 }
{stu59 }
{stu25 }
{stu0 }
{stu62 }
{stu74 }
{stu37 }
{stu66 }
{stu47 }
{stu88 } {stu0 }
{stu25 }
{stu37 }
{stu47 }
{stu59 }
{stu62 }
{stu66 }
{stu74 }
{stu81 }
{stu88 }
[root@NEO project]# // 任何类型,只要实现了它的规范(接口),就可以调用它的函数来排序

断言

// 示例代码:
package main import (
"fmt"
) func main(){
var a interface{} // 定义一个空接口
var b int
a = b
c := a.(int) // 断言;把 接口a 转成 int 型
fmt.Printf("%d %T\n",c,c) d,ok := a.(string) // 断言(加判断);检查是否能转成 string 类型,并带判断
if ok == false {
fmt.Println("convert failed")
return
}
fmt.Println("%d :string type",d)
} // 运行结果:
[root@NEO example01_interface_assert]# go run main/main.go
int
convert failed
[root@NEO example01_interface_assert]#

类型断言:(采用type switch方式)

写一个函数判断传入参数的类型

// 示例代码:
package main import "fmt" func classifier(items...interface{}){
for i,x := range(items){
switch x.(type){ // 接口.(type) 要和 switch 一起用
case bool:
fmt.Printf("param #%d is a bool,val is %v\n", i,x)
case float64:
fmt.Printf("param #%d is a float64,val is %v\n", i,x)
case int,int64:
fmt.Printf("param #%d is a int,val is %v \n",i,x)
case nil:
fmt.Printf("param #%d is nil,val is %v\n", i,x)
case string:
fmt.Printf("param #%d is a string,val is %v\n", i,x)
default:
fmt.Printf("param #%d’s type is unknown,val is %v\n", i,x)
}
}
} func main() {
var a int
classifier(a,"abc",3.3)
} // 运行结果:
[root@NEO example01_interface_assert02]# go run main/main.go
param # is a int,val is
param # is a string,val is abc
param # is a float64,val is 3.3
[root@NEO example01_interface_assert02]#

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

// 示例代码:
package main import "fmt" type Stringer interface {
Strings() string
} type MyStruct struct{
Name string
Id int
} func (p *MyStruct) Strings() string{
return p.Name
} func main() {
var vp *MyStruct = &MyStruct{
Name:"mystruct",
Id: ,
} var t interface{}
t = vp // 要判断变量是否实现了某个接口,必须要选择该变量赋值给一个接口再调用下面的方法 if v,ok := t.(Stringer);ok { // 此处只能 t.(Stringer),即只能用接口去调用 .(接口名) (把变量赋值给接口再判断);返回两个值:接口t指向的变量 和 判断结果(true 或 false)
fmt.Printf("vp implements Strings():%s; v:%v; ok:%v\n",v.Strings(),v,ok)
}
} // 运行结果:
[root@NEO example01_interface_var2interface]# go run main/main.go
vp implements Strings():mystruct; v:&{mystruct }; ok:true
[root@NEO example01_interface_var2interface]#

实现一个通用的链表类

// 目录结构如下:
example01_interface_linkedlist
├── linkedlist.go
└── main.go // linkedlist.go 代码如下:
package main import "fmt" type LinkNode struct { // 定义一个节点类
data interface{} // 因为 data 要存任何类型的数据,所以要用空接口
next *LinkNode
} type Linkedlist struct{ // 定义一个链表类
head *LinkNode // 定义链表头节点
tail *LinkNode // 定义链表尾节点
} func (p *Linkedlist) HeadInsert(data interface{}){ //头插法; data 要能够接收任何类型的数据,所以要用接口类型
node := &LinkNode{ // 先生成一个节点
data:data,
next:nil,
} if (p.head == nil && p.tail == nil){ // 此时链表为空
p.tail = node
p.head = node // 头尾节点此时都赋值为 node
return
} node.next = p.head // 链表头作为新生成节点的 next
p.head = node // 新的节点成为链表头
} func (p *Linkedlist) TailInsert(data interface{}){ // 尾插法
node := &LinkNode{ // 先生成一个节点
data:data,
next:nil,
} if (p.head == nil && p.tail == nil){ // 此时链表为空
p.tail = node
p.head = node // 头尾节点此时都赋值为 node
return
} p.tail.next = node // 新生成节点先成为原尾节点的 next
p.tail = node // 新节点成为尾节点
} func (p *Linkedlist) Traversal(){ // 遍历链表
node := p.head
for (node != nil){
fmt.Println(node.data)
node = node.next
}
} // main.go 代码如下:
package main func main(){
var intLink *Linkedlist = new(Linkedlist)
for i := ; i < ; i++{
// intLink.HeadInsert(i)
intLink.TailInsert(i)
} intLink.Traversal()
} // 头插法编译后运行结果如下:
[root@NEO project]# go build -o bin/example01_interface_linkedlist go_dev/day06/example01_interface_linkedlist
[root@NEO project]# bin/example01_interface_linkedlist [root@NEO project]#

interface{},接口中一个方法也没有,所以任何类型都实现了空接口,也就是任何变量都可以赋值给空接口。

变量slice和接口slice之间赋值操作,要用for range 一个个元素赋值

var a []int
var b []interface{}
// b = a // 不能直接 a 赋值给了

反射

// 反射:可以在运行时动态获取变量的相关信息
Import ("reflect") 反射相关函数:
reflect.TypeOf // 获取变量的类型,返回reflect.Type类型
reflect.ValueOf // 获取变量的值,返回reflect.Value类型
reflect.Value.Kind // 获取变量的类别,返回一个常量
reflect.Value.Interface() // 转换成interface{}类型 // 变量 <--> Interface{} <--> reflect.Value

示例代码:

package main

import (
"fmt"
"reflect"
) type Student struct{
Name string
Age int
Score float64
} func test(b interface{}){
t := reflect.TypeOf(b) // 获取变量的类型
fmt.Println(t) // main.Student 类型 v := reflect.ValueOf(b) // 返回 reflect.Value 类型,可利用这个 Value 类型 及相对应的方法 对这个变量进行更深入的分析( 是浮点型,就能获取浮点型的值,int型能获取int的值)
fmt.Println(v) // {stu01 18 80} k := v.Kind() // 返回 reflect.Value 的类别
fmt.Println(k) // struct iv := v.Interface() // 再把 reflect.Value 类型转化为 接口类型
stu, ok := iv.(Student) // 判断 iv 是不是指向 Student 类型;如果是 就转化为 Student 类型的值
if ok == true {
fmt.Printf("%v %T\n",stu,stu) // 打印结果: {stu01 18 80} main.Student
}
} func TestInt(b interface{}){
v := reflect.ValueOf(b)
val := v.Int() // 获取int值(如果是 int类型)
fmt.Printf("get value of interface{} %d\n",val) // 打印结果: get value of interface{} 123
} func main(){
var stu Student = Student{
Name:"stu01",
Age:,
Score:,
}
test(stu) TestInt()
} // 运行结果:
[root@NEO example02_reflect_01]# go run main/main.go
main.Student
{stu01 }
struct
{stu01 } main.Student
get value of interface{}
[root@NEO example02_reflect_01]#

reflect.Value.Kind()方法返回的常量

const (
Invalid Kind = iota
Bool
Int
Int8
Int16
Int32
Int64
Uint
Uint8
Uint16
Uint32
Uint64
Uintptr
Float32
Float64
Complex64
Complex128
Array
Chan
Func
Interface
Map
Ptr
Slice
String
Struct
UnsafePointer
)

获取变量的值:

reflect.ValueOf(x).Float()         // 获取 float 型的值
reflect.ValueOf(x).Int() // 获取 int 型的值
reflect.ValueOf(x).String() // 获取 string 型的值
reflect.ValueOf(x).Bool() // 获取 bool 型的值

通过反射的来改变变量的值:

reflect.Value.SetXX 相关方法,比如:
reflect.Value.SetFloat() // 设置浮点数
reflect.Value.SetInt() // 设置整数
reflect.Value.SetString() // 设置字符串

示例代码:

package main

import (
"fmt"
"reflect"
) func SetVal(data interface{}){
v := reflect.ValueOf(data) // SetVal(&b) 传入的是一个指针,获取到的 val 也是一个指针(指针型的Value)
fmt.Println(v)
v.Elem().SetInt() // v.Elem() 作用即 相当于 *指针 ,即该指针指向变量的 Value (reflect.Value 型,可理解为一个结构体); 把值设置成 10;由于v是一个指针,此处不能直接用 v.SetInt() } func main(){
var b int =
SetVal(&b) // 此处不能只传入 b,只传b传入的是复本,修改复本不会改变原来值的大小, 调用 SetInt()时会 panic
fmt.Println(b)
} // 运行结果:
[root@NEO example02_reflect02_setval]# go run main/main.go
0xc000016098 [root@NEO example02_reflect02_setval]#

用反射操作结构体

reflect.Value.NumField()        // 获取结构体中字段的个数
reflect.Value.Method(n).Call // 来调用结构体中的方法(n表示下标,可理解成第n个方法);.Call 的参数是 reflect.Value 型的切片,返回值也是 reflect.Value 型的切片

示例代码:

package main

import (
"fmt"
"reflect"
) type Student struct{
Name string `json:"student_name"`
Age int
Score float64
} func (p *Student) Print(){
fmt.Println("*p ------->",*p)
} func (p *Student) Set(name string,age int,score float64){
p.Name = name
p.Age = age
p.Score = score
} func HandleStruct(data interface{}){
fmt.Printf("v:%v T:%T\n",data,data)
tye := reflect.TypeOf(data) // 指针型的 reflect.Type v := reflect.ValueOf(data)
fmt.Printf("value:%v type:%T\n",v,v)
kd := v.Elem().Kind()
if kd != reflect.Struct { // Struct 是 reflect 这个包中的常量;先判断 v的类别 是不是 结构体类型
fmt.Println("struct expected")
return
} numField := v.Elem().NumField() // reflect.Value.NumField() ---> 获取结构体字段的个数
fmt.Println("struct field number:",numField) for i := ; i < v.Elem().NumField();i++ { // 获取字段的值: reflect.Value.Field(i)
fmt.Printf("i: %d v_T: %T v: %v\n",i, v.Elem().Field(i).Kind(), v.Elem().Field(i))
} // 获取tag (json.Marshal()的原理)
tag := tye.Elem().Field().Tag.Get("json") // tye.Elem() ---> 指针指向变量的 reflect.Type
fmt.Println("tag:",tag) numMethod := v.NumMethod() // reflect.Value.NumMethod() ---> 获取结构体的方法数;由于 Set 传入的是 Student 的指,所以是 v.Set()
fmt.Println("struct method number:",numMethod) v.Method().Call(nil) // 传入 nil params := make([]reflect.Value,) // 声明并初始化 reflect.Value 型的切片(该切片要作为 .Call() 的参数)
params[] = reflect.ValueOf("neo") // 任何类型的数据经过 reflect.ValueOf()处理之后都会得到相应的 reflect.Value 类型
params[] = reflect.ValueOf()
params[] = reflect.ValueOf(98.5)
v.Method().Call(params)
fmt.Printf("*data v:%v *data T:%T data v:%v data T:%T\n",data,data,*(data.(*Student)),data.(*Student))
// 形参 data 是一个接口,调用 HandleStruct() 函数时传入的 &stu 是一个指针,data 是一个指向指针的接口,接口 data 前不能直接加 * 取值, data.(*Student) 通过断言 获取到 data的指针型数据,再利用 *指针 获取指针指向的值 (应该也可以通过 reflect.ValueOf(data).Elem() 来获取指针指向变量的Value型,然后再获取其值)
} func main(){
var stu Student = Student{
Name:"stu01",
Age:,
Score:85.5,
}
HandleStruct(&stu)
} // 运行结果:
[root@NEO example02_reflect_handleStruct]# go run main/main.go
v:&{stu01 85.5} T:*main.Student
value:&{stu01 85.5} type:reflect.Value
struct field number:
i: v_T: reflect.Kind v: stu01
i: v_T: reflect.Kind v:
i: v_T: reflect.Kind v: 85.5
tag: student_name
struct method number:
*p -------> {stu01 85.5}
*data v:&{neo 98.5} *data T:*main.Student data v:{neo 98.5} data T:*main.Student
[root@NEO example02_reflect_handleStruct]#

golang(6): 接口 & 反射的更多相关文章

  1. Golang通脉之反射

    什么是反射 官方关于反射定义: Reflection in computing is the ability of a program to examine its own structure, pa ...

  2. golang中的反射reflect详解

    先重复一遍反射三定律: 1.反射可以将"接口类型变量"转换为"反射类型对象". 2.反射可以将"反射类型对象"转换为"接口类型变量 ...

  3. [golang note] 接口使用

    侵入式接口 √ 在其他一些编程语言中,接口主要是作为不同组件之间的契约存在,即规定双方交互的规约. √ 对契约的实现是强制的,即必须确保用户的确实现了该接口,而实现一个接口,需要从该接口继承. √ 如 ...

  4. golang(08)接口介绍

    原文链接 http://www.limerence2017.com/2019/09/12/golang13/#more 接口简介 golang 中接口是常用的数据结构,接口可以实现like的功能.什么 ...

  5. 七、golang中接口、反射

    一.接口定义 1.定义 interface类型可以定义一组方法,但是这些不需要实现,并且interface不能包含任何变量 package main import ( "fmt" ...

  6. golang:reflect反射

    因为之前一直以C++为主要开发语言,所以刚接触go语言中的reflect时感觉很懵逼,因此决定找资料彻底学习一下. 到底反射是什么? https://blog.golang.org/laws-of-r ...

  7. golang基础--reflect反射

    反射的知识点比较晦涩,后期会对此知识点展开深入的分析及示例代码展示 反射可达大提高程序的灵活性,使得inferface{}有更大的发挥余地 反射使用TypeOf和ValueOf函数从接口中获取目标对象 ...

  8. Golang之接口(interface)

    Golang最重要的接口,,,, package main import ( "fmt" ) //interface类型默认是指针 /* 接口的实现 Golang中的接口,不需要显 ...

  9. golang中接口interface和struct结构类的分析

    再golang中,我们要充分理解interface和struct这两种数据类型.为此,我们需要优先理解type的作用. type是golang语言中定义数据类型的唯一关键字.对于type中的匿名成员和 ...

随机推荐

  1. Zookeeper 安装及命令行操作

    [参考文章]:[分布式]Zookeeper使用--命令行 [参考文章]:zookeeper的数据模型 [参考文章]:zookeeper ACL使用 1. 安装包下载 官方下载地址 选择一个具体的版本进 ...

  2. parse_url小结

    本篇文章对parse_url进行一个小结 0x01:parse_url $url = "/baidu.com:80"; $url1 = "/baidu.com:80a&q ...

  3. JS基础_对象操作

    1.in 运算符 通过该运算符可以检查一个对象中是否含有指定的属性,如果有,返回true 语法: “属性名” in 对象 var obj = { name:"hello" } co ...

  4. How to Fix Grub error: no such partition Grub Rescue

    错误信息: error: no such partition Entering rescue mode... grub rescue> _ 错误原因: grub找不到文件normal.mod 解 ...

  5. js 执行总结1

    一. function sayHi() { let x; let y; try { throw new Error(); } catch (x) { // 局部 x = 1; console.log( ...

  6. Utf8 与 Utf8-BOM 的差异

    1.先说差异,Utf8-BOM编码的字符串比Utf8编码的字符串,多了前缀\xEF\xBF\xBD,肉眼是看不出来的,解析出来的字符串的长度也正常 比如:"123456",Enco ...

  7. 提升键盘可访问性和AT可访问性

    概述 很多地方比如官网中需要提升 html 的可访问性,我参考 element-ui,总结了一套提升可访问性的方案,记录下来,供以后开发时参考,相信对其他人也有用. 可访问性 可访问性基本上分为 2 ...

  8. python 连接mysql数据库:pymysql

    示例:import pymysql conn=pymysql.connect( host="127.0.0.1", #数据库IP port=3306, #数据库端口 user=&q ...

  9. [redis]redis实现分页的方法

    每个主题下的用户的评论组装好写入Redis中,每个主题会有一个topicId,每一条评论会和topicId关联起来,大致的数据模型如下:{ topicId: 'xxxxxxxx', comments: ...

  10. 只含有一个Excel模板的工程发布问题

    遇到这样一个问题,某个项目不是dynamic web project,也不是java工程,里面只有一个Excel模板,这样的话,不能打成war包和jar包,不能通过eclipse发布至Tomcat,但 ...