浅谈Go语言中的结构体struct & 接口Interface & 反射
结构体struct
struct 用来自定义复杂数据结构,可以包含多个字段(属性),可以嵌套;
go中的struct类型理解为类,可以定义方法,和函数定义有些许区别;
struct类型是值类型。
struct定义
type user struct { name string age int32 mess string }
var user user var user1 *user = &user{} var user2 *user = new(user)
struct使用
下面示例中user1和user2为指针类型,访问的时候编译器会自动把 user1.name 转为 (*user1).name
func main() { var user user user.name = "nick" user.age = 18 user.mess = "lover" var user1 *user = &user{ name: "dawn", age: 21, } fmt.println(*user1) //{dawn 21 } fmt.println(user1.name, (*user1).name) //dawn dawn var user2 *user = new(user) user2.name = "suoning" user2.age = 18 fmt.println(user2) //&{suoning 18 } fmt.println(user2.name, (*user2).name) //suoning suoning }
构造函数
golang中的struct没有构造函数,可以伪造一个
type user struct { name string age int32 mess string } func newuser(name string, age int32, mess string) *user { return &user{name:name,age:age,mess:mess} } func main() { //user := new(user) user := newuser("suoning", 18, "lover") fmt.println(user, user.mess, user.name, user.age) }
内存布局
struct中的所有字段在内存是连续的
var user user user.name = "nick" user.age = 18 user.mess = "lover" fmt.println(user) //{nick 18 lover} fmt.printf("name:%p\n", &user.name) //name:0xc420016180 fmt.printf("age: %p\n", &user.age) //age: 0xc420016190 fmt.printf("mess:%p\n", &user.mess) //mess:0xc420016198 8字节为内存对齐
方法
方法是作用在特定类型的变量上,因此自定义类型,都可以有方法,而不仅仅是struct。
方法的访问控制也是通过大小写控制。
init函数是通过传入指针实现,这样改变struct字段值,因为是值类型。
type user struct { name string age int sex string } func (this *user) init(name string, age int, sex string) { this.name = name this.age = age this.sex = sex } func (this user) getname() string { return this.name } func main() { var user user user.init("nick", 18, "man") //(&user).init("nick", 18, "man") name := user.getname() fmt.println(name) }
匿名字段
如果有冲突的, 则最外的优先
type user struct { name stirng age int } type lover struct { user sex time.time int age int }
继承 & 多重继承
一个结构体继承多个结构体,访问通过点。继承字段以及方法。
可以起别名,如下面 u1(user1),访问 user.u1.age。
如果继承的结构体都拥有同一个字段,通过user.name访问就会报错,必须通过user.user1.name来访问。
type user1 struct { name string age int } type user2 struct { name string age int sex time.time } type user struct { u1 user1 //别名 user2 name string age int } func main() { var user user user.name = "nick" user.u1.age = 18 fmt.println(user) //{{ 18} { 0 {0 0 <nil>}} nick 0} }
tag
在go中,首字母大小写有特殊的语法含义,小写包外无法引用。由于需要和其它的系统进行数据交互,例如转成json格式。这个时候如果用属性名来作为键值可能不一定会符合项目要求。tag在转换成其它数据格式的时候,会使用其中特定的字段作为键值。
import "encoding/json" type user struct { name string `json:"username"` age int `json:"userage"` } func main() { var user user user.name = "nick" user.age = 18 conjson, _ := json.marshal(user) fmt.println(string(conjson)) //{"username":"nick","userage":0} }
string()
如果实现了string()这个方法,那么fmt默认会调用string()。
type name1 struct { int string } func (this *name1) string() string { return fmt.sprintf("this is string(%s).", this.string) } func main() { n := new(name1) fmt.println(n) //this is string(). n.string = "suoning" d := fmt.sprintf("%s", n) //this is string(suoning). fmt.println(d) }
接口interface
interface类型可以定义一组方法,但是这些不需要实现。并且interface不能包含任何变量。
interface类型默认是一个指针。
interface定义
type car interface { nameget() string run(n int) stop() }
interface实现
golang中的接口,不需要显示的实现。只要一个变量,含有接口类型中的所有方法,那么这个变量就实现这个接口。因此,golang中没有implement类似的关键字;
如果一个变量含有了多个interface类型的方法,那么这个变量就实现了多个接口;如果一个变量只含有了1个interface的方部分方法,那么这个变量没有实现这个接口。
空接口 interface{}:空接口没有任何方法,所以所有类型都实现了空接口。
var a int var b interface{} //空接口 b = a
多态
一种事物的多种形态,都可以按照统一的接口进行操作。
栗子:
type car interface { nameget() string run(n int) stop() } type bmw struct { name string } func (this *bmw) nameget() string { return this.name } func (this *bmw) run(n int) { fmt.printf("bmw is running of num is %d \n", n) } func (this *bmw) stop() { fmt.printf("bmw is stop \n") } type benz struct { name string } func (this *benz) nameget() string { return this.name } func (this *benz) run(n int) { fmt.printf("benz is running of num is %d \n", n) } func (this *benz) stop() { fmt.printf("benz is stop \n") } func (this *benz) chatup() { fmt.printf("chatup \n") } func main() { var car car fmt.println(car) // <nil> var bmw bmw = bmw{name: "宝马"} car = &bmw fmt.println(car.nameget()) //宝马 car.run(1) //bmw is running of num is 1 car.stop() //bmw is stop benz := &benz{name: "大奔"} car = benz fmt.println(car.nameget()) //大奔 car.run(2) //benz is running of num is 2 car.stop() //benz is stop //car.chatup() //error: car.chatup undefined (type car has no field or method chatup) }
interface嵌套
一个接口可以嵌套在另外的接口。
即需要实现2个接口的方法。
type car interface { nameget() string run(n int) stop() } type used interface { car cheap() }
类型断言
类型断言,由于接口是一般类型,不知道具体类型,
如果要转成具体类型,可以采用以下方法进行转换:
var t int var x interface{} x = t y = x.(int) //转成int y, ok = x.(int) //转成int,不报错
栗子一:
func test(i interface{}) { // n := i.(int) n, ok := i.(int) if !ok { fmt.println("error") return } n += 10 fmt.println(n) } func main() { var t1 int test(t1) }
栗子二:
switch & type type student struct { name string } func judgmenttype(items ...interface{}) { for k, v := range items { switch v.(type) { case string: fmt.printf("string, %d[%v]\n", k, v) case bool: fmt.printf("bool, %d[%v]\n", k, v) case int, int32, int64: fmt.printf("int, %d[%v]\n", k, v) case float32, float64: fmt.printf("float, %d[%v]\n", k, v) case student: fmt.printf("student, %d[%v]\n", k, v) case *student: fmt.printf("student, %d[%p]\n", k, v) } } } func main() { stu1 := &student{name: "nick"} judgmenttype(1, 2.2, "learing", stu1) }
栗子三:
判断一个变量是否实现了指定接口
type stringer interface { string() string } type mystruct interface { } type mystruct2 struct { } func (this *mystruct2) string() string { return "" } func main() { var v mystruct var v2 mystruct2 v = &v2 if sv, ok := v.(stringer); ok { fmt.printf("%v implements string(): %s\n", sv.string()); } }
反射 reflect
reflect包实现了运行时反射,允许程序操作任意类型的对象。
典型用法是用静态类型interface{}保存一个值,
通过调用typeof获取其动态类型信息,该函数返回一个type类型值。
调用valueof函数返回一个value类型值,该值代表运行时的数据。
func typeof(i interface{}) type
typeof返回接口中保存的值的类型,typeof(nil)会返回nil。
func valueof(i interface{}) value
valueof返回一个初始化为i接口保管的具体值的value,valueof(nil)返回value零值。
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.value.kind()方法返回的常量
reflect.value.interface()
转换成interface{}类型
【变量<-->interface{}<-->reflect.value】
获取变量的值:
reflect.valueof(x).int() reflect.valueof(x).float() reflect.valueof(x).string() reflect.valueof(x).bool()
通过反射的来改变变量的值
reflect.value.setxx相关方法,比如: reflect.value.setint(),设置整数 reflect.value.setfloat(),设置浮点数 reflect.value.setstring(),设置字符串
栗子一
import "reflect" func main() { var x float64 = 5.21 fmt.println("type:", reflect.typeof(x)) //type: float64 v := reflect.valueof(x) fmt.println("value:", v) //value: 5.21 fmt.println("type:", v.type()) //type: float64 fmt.println("kind:", v.kind()) //kind: float64 fmt.println("value:", v.float()) //value: 5.21 fmt.println(v.interface()) //5.21 fmt.printf("value is %1.1e\n", v.interface()) //value is 5.2e+00 y := v.interface().(float64) fmt.println(y) //5.21 }
栗子二(修改值)
setxx(x) 因为传递的是 x 的值的副本,所以setxx不能够改 x,改动 x 必须向函数传递 x 的指针,setxx(&x) 。
//错误代码!!! //panic: reflect: reflect.value.setfloat using unaddressable value func main() { var a float64 fv := reflect.valueof(&a) fv.setfloat(520.00) fmt.printf("%v\n", a) }
//正确的,传指针 func main() { var a2 float64 fv2 := reflect.valueof(&a2) fv2.elem().setfloat(520.00) fmt.printf("%v\n", a2) //520 }
反射操作结构体
reflect.value.numfield()获取结构体中字段的个数
reflect.value.method(n).call(nil)来调用结构体中的方法
栗子一(通过反射操作结构体)
import "reflect" type notknowntype struct { s1 string s2 string s3 string } func (n notknowntype) string() string { return n.s1 + " & " + n.s2 + " & " + n.s3 } var secret interface{} = notknowntype{"go", "c", "python"} func main() { value := reflect.valueof(secret) fmt.println(value) //go & c & python typ := reflect.typeof(secret) fmt.println(typ) //main.notknowntype knd := value.kind() fmt.println(knd) // struct for i := 0; i < value.numfield(); i++ { fmt.printf("field %d: %v\n", i, value.field(i)) } results := value.method(0).call(nil) fmt.println(results) // [go & c & python] }
栗子二(通过反射修改结构体)
import "reflect" type t struct { a int b string } func main() { t := t{18, "nick"} s := reflect.valueof(&t).elem() typeoft := s.type() for i := 0; i < s.numfield(); i++ { f := s.field(i) fmt.printf("%d: %s %s = %v\n", i, typeoft.field(i).name, f.type(), f.interface()) } s.field(0).setint(25) s.field(1).setstring("nicky") fmt.println(t) } /* 输出: 0: a int = 18 1: b string = nick {25 nicky} */
import "reflect" type test struct { s1 string s2 string s3 string } var s interface{} = &test{ s1: "s1", s2: "s2", s3: "s3", } func main() { val := reflect.valueof(s) fmt.println(val) //&{s1 s2 s3} fmt.println(val.elem()) //{s1 s2 s3} fmt.println(val.elem().field(0)) //s1 val.elem().field(0).setstring("hehe") //s1大写 }
栗子三(struct tag 内部实现)
package main import ( "fmt" "reflect" ) type user struct { name string `json:"user_name"` } func main() { var user user usertype := reflect.typeof(user) jsonstring := usertype.field(0).tag.get("json") fmt.println(jsonstring) //user_name }
以上这篇浅谈go语言中的结构体struct & 接口interface & 反射就是小编分享给大家的全部内容了,希望能给大家一个参考,也希望大家多多支持。