Go:struct类型,method方法,interface接口和reflect反射

时间:Aug. 14, 2018 分类:

目录:

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