Go:struct类型,method方法,interface接口和reflect反射
目录:
struce
- 使用
type <Name> struct
定义,名称遵循可见性规则 - 支持指向自身的指针型成员
- 支持匿名结构,可用作成员或定义成员变量
- 匿名结构也可用作map值
- 可以使用字面值对结构进行初始化
- 允许直接通过指针来读写结构成员
- 相同类型的成员可以直接拷贝赋值
- 支持
==
和!=
比较运算 - 支持匿名字段,本质上是定义了以某个类型名为名称的字段
- 嵌入结构作为匿名字段看起来像继承,但是不是继承
- 可以使用匿名字段指针
创建struct
package main
import (
"fmt"
)
type person struct (
Name string
Age int
)
func main() {
a := person()
fmt.Println(a)
}
可以看到struct在创建的时候已经复制内部字段为零值了
[root@why 01:07:55 go_code]#go run struct_test1.go
{ 0}
struct赋值
先创建再赋值
package main
import (
"fmt"
)
type person struct {
Name string
Age int
}
func main() {
a := person{}
a.Name = "why"
a.Age = 24
fmt.Println(a)
}
打印一下
[root@why 01:09:30 go_code]#go run struct_test1.go
{why 24}
直接赋值
当然也可以创建时直接赋值
package main
import (
"fmt"
)
type person struct {
Name string
Age int
}
func main() {
a := person{
Name : "why",
Age : 24,
}
fmt.Println(a)
}
传递时是值还是值的拷贝
package main
import (
"fmt"
)
type person struct {
Name string
Age int
}
func main() {
a := person{
Name : "why",
Age : 24,
}
ChangeAge(a)
fmt.Println(a)
}
// 修改年龄
func ChangeAge(per person) {
per.Age = 25
fmt.Println(per)
}
答案是值的拷贝
[root@why 01:15:13 go_code]#go run struct_test1.go
{why 25}
{why 24}
这样就需要传递一下指针
package main
import (
"fmt"
)
type person struct {
Name string
Age int
}
func main() {
a := person{
Name : "why",
Age : 24,
}
ChangeAge(&a)
fmt.Println(a)
}
func ChangeAge(per *person) {
per.Age = 25
fmt.Println(per)
}
struct中的值就被修改了
[root@why 01:20:48 go_code]#go run struct_test1.go
&{why 25}
{why 25}
package main
import (
"fmt"
)
type person struct {
Name string
Age int
}
func main() {
a := &person{
Name : "why",
Age : 24,
}
ChangeAge1(a)
ChangeAge2(a)
fmt.Println(a)
}
func ChangeAge1(per *person) {
per.Age = 25
fmt.Println(per)
}
func ChangeAge2(per *person) {
per.Age = 26
fmt.Println(per)
}
如果直接使用指针修改两次就是成功两次,因为指针是内存地址
[root@why 01:23:02 go_code]#go run struct_test1.go
&{why 25}
&{why 26}
&{why 26}
匿名结构
package main
import (
"fmt"
)
func main() {
a := struct{
Name string
Age int
}{
Name : "why",
Age : 24,
}
fmt.Println(a)
}
输出结果
[root@why 01:31:43 go_code]#go run struct_test1.go
{why 24}
匿名struct嵌套
package main
import (
"fmt"
)
type person struct {
Name string
Age int
Contact struct {
City, Phone string
}
}
func main() {
a := person{}
fmt.Println(a)
}
执行结果
[root@why 01:35:01 go_code]#go run struct_test1.go
{ 0 { }}
进行赋值
package main
import (
"fmt"
)
type person struct {
Name string
Age int
Contact struct {
City, Phone string
}
}
func main() {
a := person{Name: "why", Age: 24}
a.Contact.Phone = "12345678900"
a.Contact.City = "Beijing"
fmt.Println(a)
}
执行结果
[root@why 01:42:24 go_code]#go run struct_test1.go
{why 24 {Beijing 12345678900}}
匿名字段
[root@why 01:43:31 go_code]#cat struct_test1.go
package main
import (
"fmt"
)
type person struct {
string
int
}
func main() {
a := person{"why", 24}
fmt.Println(a)
}
[root@why 01:43:33 go_code]#go run struct_test1.go
{why 24}
嵌入结构
package main
import (
"fmt"
)
type human struct {
Sex int
}
type person struct {
human
Name string
Age int
}
func main() {
a := person{Name: "why", Age: 24, human: human{Sex: 0}}
fmt.Println(a)
}
执行结果
[root@why 01:46:57 go_code]#go run struct_test1.go
{{0} why 24}
可以允许重名
[root@why 01:50:10 go_code]#cat struct_test1.go
package main
import (
"fmt"
)
type A struct {
B
C
}
type B struct {
Name string
}
type C struct {
Name string
}
func main() {
a := A{B: B{Name: "why"}, C: C{Name: "why"}}
fmt.Println(a)
}
[root@why 01:50:19 go_code]#go run struct_test1.go
{{why} {why}}
方法method
- 通过显示说明receiver来实现与某个类型的组合
- 只能为同一个包中的类型来定义方法
- Receiver可以是类型的值或者指针
- 不存在方法重载
- 可以通过值或指针的方式来调用方法,编译器会自动完成转换
- 如果外部结构和嵌入结构存在同名方法,优先调用外部方法
- 类型别名不会拥有底层类型所附带的方法
- 方法可以调用结构中非公开字段
创建method
方式是使用func <接收者 struct> <方法名>
package main
import (
"fmt"
)
type A struct {
Name String
}
func (a A) Print() {
fmt.Println("A")
}
func main() {
a := A{}
a.Print()
}
执行结果
[root@why 02:01:45 go_code]#go run method_test1.go
A
传递的是值的拷贝还是引用?
package main
import (
"fmt"
)
type A struct {
Name string
}
type B struct {
Name string
}
func (a *A) Print() {
a.Name = "AA"
fmt.Println("A")
}
func (b B) Print() {
b.Name = "BB"
fmt.Println("B")
}
func main() {
a := A{}
a.Print()
fmt.Println(a.Name)
b := B{}
b.Print()
fmt.Println(b.Name)
}
执行结果
[root@why 02:06:45 go_code]#go run method_test1.go
A
AA
B
类型别名
类型别名只是拷贝数据类型,但是方法不会被拷贝
package main
import (
"fmt"
)
type A int
func (a *A) Print() {
fmt.Println("A")
}
func main() {
var a A
a.Print()
}
执行结果
[root@why 18:47:55 go_code]#go run method_test2.go
A
但是为什么不能使用a := A{}
另外属性的访问权限,对于同一个包,都是有访问权限的,如果是其他包,必须是首字母大写的才有访问权限
也可以使用
(*A).Print(&a)
强制类型转换
package main
import (
"fmt"
)
type A int
func (a *A) Increase(num int) {
*a += A(num)
}
func main() {
var a A
a.Increase(100)
fmt.Println(a)
}
执行结果
[root@why 19:01:12 go_code]#go run method_test2.go
100
接口interface
- 接口是一个或多个方法的签名集合
- 只要某个类型拥有接口的方法签名,即算实现该接口,无需声明实现了那些接口
- 接口只有方法声明,没有实现,没有数据字段
- 接口可以匿名嵌入其他接口,或者嵌入结构
- 将对象赋值给接口时,会发生拷贝,而接口内部存储的是指向这个复制品的指针,即无法修改复制品,也无法获取指针
- 只有当接口存储的数据类型和对象都为nil时,接口才等于nil
- 接口调用不会做receiver的自动转换
- 接口同样支持匿名字段方法
- 空接口可以实现类似OOP中的多态
- 空接口可以作为任何类型数据的容器
定义接口
package main
import (
"fmt"
)
// 定义struct
type Phone struct {
name string
}
// 定义接口
type USB interface {
Name() string
Connect()
}
// 定义接口方法
func (ph Phone) Name() string {
return ph.name
}
// 定义接口方法
func (ph Phone) Connect() {
fmt.Println("Connect: "+ph.name)
}
func main() {
var a USB
a = Phone{name: "OnePlue5"}
fmt.Println(a.Name())
a.Connect()
}
执行结果
[root@why 23:20:27 go_code]#go run interface_test1.go
OnePlue5
Connect: OnePlue5
匿名嵌入接口
package main
import (
"fmt"
)
// 定义struct
type Phone struct {
name string
}
// 定义接口
type USB interface {
Name() string
Connect
}
// 用于嵌入的接口
type Connect interface {
Connect()
}
// 定义接口方法
func (ph Phone) Name() string {
return ph.name
}
// 定义接口方法
func (ph Phone) Connect() {
fmt.Println("Connect: "+ph.name)
}
func main() {
var a USB
a = Phone{name: "OnePlue5"}
fmt.Println(a.Name())
a.Connect()
}
类型断言
package main
import (
"fmt"
)
// 定义struct
type Phone struct {
name string
}
// 定义接口
type USB interface {
Name() string
Connect
}
// 用于嵌入的接口
type Connect interface {
Connect()
}
// 定义接口方法
func (ph Phone) Name() string {
return ph.name
}
// 定义接口方法
func (ph Phone) Connect() {
fmt.Println("Connect: "+ph.name)
}
func main() {
// 直接定义
a := Phone{name: "OnePlue5"}
fmt.Println(a.Name())
a.Connect()
Disconnect(a)
}
func Disconnect(usb USB) {
// 判断类型
if ph, ok := usb.(Phone); ok {
fmt.Println("Disconnected:"+ph.name)
}else{
fmt.Println("Unknown device.")
}
}
使用use.(Phone)进行断言
[root@why 23:42:30 go_code]#go run interface_test1.go
OnePlue5
Connect: OnePlue5
Disconnected:OnePlue5
空接口
// 定义接口
type empty interface {
}
在golang中,任何类型都符合空接口,即实现了空接口。
对于接口的实现原理,任何符合接口要求,就实现了这个接口。
将上边的程序改造一下,传入一个空接口,因为任何类型都实现了空接口,Phone类型也实现了。
func Disconnect(usb interface{}) {
// 判断类型
if ph, ok := usb.(Phone); ok {
fmt.Println("Disconnected:"+ph.name)
}else{
fmt.Println("Unknown device.")
}
}
接口适用多个类型
可以通过switch来进行判断
func Disconnect(usb interface{}) {
// 判断类型
switch v := usb.(type) {
case Phone:
fmt.Println("Disconnected:"+v.name)
default:
fmt.Println("Unknown device.")
}
}
usb.(type)
通过系统来进行判断
强制类型转换
只能是上级的接口转换为下级的接口
func main() {
// 直接定义
var a USB
a = Phone{name: "OnePlue5"}
a.Connect()
var b Connect
b = Connect(a)
b.Connect()
}
执行结果
[root@why 00:07:21 go_code]#go run interface_test1.go
Connect: OnePlue5
Connect: OnePlue5
反过来,下级接口转换为上级接口
func main() {
// 直接定义
var a Connect
a = Phone{name: "OnePlue5"}
a.Connect()
var b USB
b = USB(a)
b.Connect()
}
执行结果
[root@why 00:08:38 go_code]#go run interface_test1.go
# command-line-arguments
./interface_test1.go:39:12: cannot convert a (type Connect) to type USB:
Connect does not implement USB (missing Name method)
传递的为值的拷贝
func main() {
pc := Phone{name: "OnePlue5"}
var a Connect
a = Connect(pc)
a.Connect()
pc.name = "OnePlue6"
a.Connect()
}
执行结果
[root@why 00:14:43 go_code]#go run interface_test1.go
Connect: OnePlue5
Connect: OnePlue5
反射reflecttion
- 反射可以提高程序的灵活性,使interface有更大的发挥余地
- 反射使用TypeOf和ValueOf函数从接口获取对象目标的信息
- 反射会将匿名字段作为独立字段
- 想要使用反射修改对象的状态,前提是interface.data是settable
- 反射可以动态调用方法
package main
import (
"fmt"
"reflect"
)
type User struct {
id int
Name string
Age int
}
func (user User) Hello() {
fmt.Println("Hello!")
}
func main() {
user := User{1, "why", 24}
info(user)
}
func info(o interface{}) {
t := reflect.TypeOf(o)
fmt.Println("Type:", t.Name())
v := reflect.ValueOf(o)
fmt.Println("Fields:")
for i := 0; i < t.NumField(); i++ {
f := t.Field(i)
//val := v.Field(i).Interface()
val := v.Field(i)
fmt.Printf("%6s: %v = %v\n", f.Name, f.Type, val)
}
for i := 0; i < t.NumMethod(); i++ {
m := t.Method(i)
fmt.Printf("%6s: %v\n", m.Name, m.Type)
}
}
执行结果
[root@why 00:46:24 go_code]#go run reflection_test1.go
Type: User
Fields:
id: int = 1
Name: string = why
Age: int = 24
Hello: func(main.User)
可以对传入的类型进行判断
if k := t.Kind(); k != reflect.Struct {
return
}
对于嵌入的struct,可以通过FieldByIndex([]int(0, 0))
的形式使用select获取
对基本类型操作
package main
import (
"fmt"
"reflect"
)
func main() {
x := 123
v := reflect.ValueOf(&x)
v.Elem().SetInt(999)
fmt.Println(x)
}
执行结果
[root@why 01:13:40 go_code]#go run reflection_test2.go
999
对自定义类型操作
package main
import (
"fmt"
"reflect"
)
type User struct {
id int
Name string
Age int
}
func main() {
user := User{1, "why", 24}
Set(&user)
fmt.Println(user)
}
func Set(o interface{}) {
v := reflect.ValueOf(o)
if v.Kind()==reflect.Ptr&&!v.Elem().CanSet(){
return
}else{
v = v.Elem()
}
if f:=v.FieldByName("Name");f.Kind()==reflect.String{
f.SetString("wanghongyu")
}
执行结果
[root@why 01:42:59 go_code]#go run reflection_test3.go
{1 wanghongyu 24}
如果没找到字段是不会报错也不会修改
可以进行一下判断
f:=v.FieldByName("Name")
if !f.IsValid(){
return
}
反射调用方法
package main
import (
"fmt"
"reflect"
)
type User struct {
id int
Name string
Age int
}
func (user User) Hello(name string) {
fmt.Println("Hello "+name)
}
func main() {
user := User{1, "why", 24}
v := reflect.ValueOf(user)
mv := v.MethodByName("Hello")
args:=[]reflect.Value{reflect.ValueOf("pqt")}
mv.Call(args)
}
执行结果
[root@why 01:51:04 go_code]#go run reflection_test4.go
Hello pqt