关于golang struct interface的理解使用

前端时间说了不少golang常用库包的使用,貌似没有详细说struct interface的概念。在说interface之前,我们知道在Golang 中是没有类class 的概念,golang是通过 interface 类型接口实现的继承多态的效果。

一个 interface 类型定义了一个方法集做接口。 使用golang实现继承时,我们只要记得要给我们的interface类型实现一个method,就完成了对interface的使用。

该文章写的有些乱,欢迎来喷 ! 另外文章后续不断更新中,请到原文地址查看更新。

http://xiaorui.cc/?p=2938

我们首先要区分出goalng的方法和函数。他们之间虽然都是通过func定义的,但还是有区别的。 

这是函数,很直接的函数。 

func go() {
    fmt.Println('go to home')
}

这是struct结构体,后面的move()函数是car结构体的一个方法。 如果move的方法类型是g *car,那么g是指针。 初始化了car后,可以直接用car的对象调用move方法。

type car struct{
    name string
    num  int
}

func (g car) move(){
    fmt.Println("driver car ,xiaorui.cc ")
}

另外在golang里某个函数想调用其他函数有这么几个用法。 方法,基于方法的interface接口,直接传参传对象。

然后来说下interface的用法:

//使用type定义一个interface

type Xiaorui interface {
    Play()
    Start(msg string)
}

// 定义a为空接口
var a interface{}
var i int = 5
s := "Hello world"
// a可以存储任意类型的数值
a = i
a = s

一个函数把interface{}作为参数,那么他可以接受任意类型的值作为参数,如果一个函数返回interface{},那么也就可以返回任意类型的值。是不是很有用啊!

这是一个golang interface的实例代码。

#xiaorui.cc
package main

import "fmt"

type S struct {
    i int
}

func (p *S) Get() int {
    return p.i
}
func (p *S) Put(v int) {
    p.i = v
}

type I interface {
    Get() int
    Put(int)
}

func f1(p I) {
    fmt.Println(p.Get())
    p.Put(888)
}

func f2(p interface{}) {
    switch t := p.(type) {
    case int:
        fmt.Println("this is int number")
    case I:
        fmt.Println("I:", t.Get())
    default:
        fmt.Println("unknow type")
    }
}

//指针修改原数据
func dd(a *S) {
    a.Put(999)
    fmt.Println(a.Get(), "in dd func")
}

//临时数据
func aa(a S) {
    a.Put(2222)
    fmt.Println(a.Get(), "in aa func")
}

func main() {
    var s S
    s.Put(333)
    fmt.Println(s.Get())
    f1(&s)
    fmt.Println(s.Get())
    f2(&s)
    dd(&s)
    fmt.Println(s.Get())
    aa(s)
    fmt.Println(s.Get())

}

运行后的结果是:

333
333
888
I: 888
999 in dd func
999
2222 in aa func
999

下面是我的自问自答,也是我学习golang中的疑问… 请忽视问题的质量.


第一个问题,为什么s可以直接调用Put函数

    首先s是用S结构体创建的,S有Get() Put()两个方法。所以s可以执行Put()

第二个问题,f函数为什么也能Get()

    因为S实现了I类型的接口,换句话说,S实现了I interface类型定义好的方法,那么I定义也就有了Get方法。

第三个问题, f2的interface{}空接口用途

    interface{}空接口可以是任何类型,我们可以在逻辑用断言的方式区别他是什么类型,然后根据类型做相应的处理。对应到上面的代码, 我给你给他传任何值,f2因为是空间口都会接收进来。
后面的 t := p.(type)是断言,所谓的断言就是区分他的type类型。  如果你不想使用switch,可以用下面的笨方法。 

func g(something interface{}){
    if t,ok := something.(I); ok{
        fmt.Println("I:",t.Get())
    }else if t,ok := something.(int); ok{
        fmt.Println("int:",t)
    }else{
        fmt.Println("not found:",something)
    }
}

空接口可代表任何类型,可做形参和返回类型

第四个问题,f1 f2 为什么要使用指针?

看代码中的类型,我被自己坑了好几次。
func (p *S) Get() int {
func (p *S) Put(v int) {

上面是interface的种种用法。下面是个google出来的一个goalng interface的例子。

#xiaorui.cc
package main

import (
 "fmt"
)

//定义了一个接口
type I interface {
 Get() int
 Put(int)
}

type S struct{ i int }

func (p *S) Get() int  { return p.i }
func (p *S) Put(v int) { p.i = v }

type R struct{ i int }

func (p *R) Get() int  { return p.i }
func (p *R) Put(v int) { p.i = v }

func f1(p I) {
     fmt.Println(p.Get())
     p.Put(1)
}

//interface{}空接口,能接受任何类型。.(I)是类型断言.
func f2(p interface{}) {
     if t, ok := p.(S); ok {
      fmt.Println("S:", t)
     } else if t, ok := p.(I); ok {
      fmt.Println("I:", t.Get())
     }
}

func f3(p interface{}) {
     switch t := p.(type) {
     case S:
      fmt.Println("S:", t.Get())
     case R:
      fmt.Println("R:", t.Get())
     case I:
      fmt.Println("I:", t.Get())
     default:
      fmt.Println("unknow type")
     }
}

func main() {
 s := S{101}

 f1(&s)
 f2(&s)

 r := R{1111}
 f3(&r)
}

最后啰嗦一下. 上面的结构S实现了I接口的两个方法,因此可以说是S实现了I接口。又因为S实现了I,因此可以f函数向其传递s,而且可以调动s的那两个方法。

对于golang interface我没有从更深的层面去讲述,为什么?   因为我也不懂太深…  以上对于interface讲述有不对的地方,欢迎来指点下。 

要看清方法是否是指针.

# command-line-arguments
  ./in.go:44: cannot use s (type S) as type I in argument to f1:
    S does not implement I (Get method has pointer receiver)

END.


大家觉得文章对你有些作用! 如果想赏钱,可以用微信扫描下面的二维码,感谢!
另外再次标注博客原地址  xiaorui.cc

2 Responses

  1. snowin 2018年1月31日 / 下午8:46

    命名变量使用毫无意义的字母,真的很难读懂

发表评论

邮箱地址不会被公开。 必填项已用*标注