golang中接口的面向对象(一)–多态特征

       最近在学习golang,虽然go并不是一个传统意义的面向对象的语言,

但是发现接口interface{}这个类型却体现了一些面向对象的特点。

        本人也是初学,参考了Go 零基础编程入门教程》– “进击的皇虫”大佬的教学。示例代码按我个人的理解稍微有所改动。

有什么不妥的地方也请大家不吝赐教。

       在学习golang的面向对象之前,我们先来了解一下 方法 method 和 接口 interface{}这两个类型:

  • 方法 method :

       在Go 语言中,我们可以在一些接收者上定义函数,这里的接收者是 自定义类型或结构体,对应为OOP中的类,

这些接收者的函数叫做方法。 

        方法(method)的声明和函数很相似, 只不过它必须指定接收者:

         func (t1 T) Func1(参数列表)  返回值类型{

         …

         }

         func (t2 *T) Func2(参数列表) 返回值类型{

         …

         }

  1. 接收者的类型只能为用关键字 type 定义的类型,例如自定义类型,结构体(其实结构体也是一种自定义类型)

  2. 同一个接收者的方法名不能重复 (没有重载),如果是结构体,方法名还不能和字段名重复。 

  3. 值作为接收者无法修改其值,如果有更改需求,需要使用指针类型。

  4. 接收者不论使用某个 类型的值还是 类型的指针,其含义都是表示绑定到某类型的方法。

例如: 

type T64 int64

func (a1 T64) Myfunc() {

    a1 = 5

}

func main() { 

    t := T64(10)

    t.Myfunc()

    fmt.Println(t)

}

输出:

10     // t的值并未被改变

接收者a1是一个自定义类型T64的对象,a1其实跟形参一样,

方法有着函数一样的性质,不能改变传入的变量的值,跟C语言一样,只有传入变量的指针,才能通过指针来修改变量。

  •  接口 interface{}:

       接口类型是一种抽象类型,是方法的集合(注意了,这里已经指出,接口是方法的集合,方法又是绑定到某个类型的函数,所以接口注定会跟其它类型扯上关系),

类型实现了这些方法就是实现了这个接口,那么我们可以把实现了这个接口的其它类型,都理解为这个接口类型的派生类型。

       

/* 定义接口 */

type interface_name interface {

method_name1 [return_type]

method_name2 [return_type]

method_name3 [return_type]

method_namen [return_type]

}

我们来看一下例子:

//定义了接口

type geometry interface {

    area() float32

    perim() float32

}

//一个自定义类型rect

type rect struct {

    len, wid float32

}

//绑定到类型rect的方法area()

func (r rect) area() float32 {

    return r.len * r.wid

}

//绑定到类型rect的方法perim()

func (r rect) perim() float32 {

    return 2 * (r.len + r.wid)

}

//一个自定义类型circle

type circle struct {

    radius float32

}

//绑定到类型circle的方法area()

func (c circle) area() float32 {

    return math.Pi * c.radius * c.radius

}

//绑定到类型circle的方法perim()

func (c circle) perim() float32 {

    return 2 * math.Pi * c.radius

}

//测试

func show(name stringparam geometry) {   //param geometry表示param可以是传递geometry的任意一个派生类型进来

    switch param.(type) {

    case geometry:

        // 类型断言

        fmt.Printf("area of %v is %v \n", name, param.area())          //param传递进来就是geometry这个接口类型,所以是可以不加上(geometry).

        fmt.Printf("perim of %v is %v \n", name, param.(geometry).perim())   //这里加上了(geometry).大家可以思考一下这个(geometry).的意义

    default:

        fmt.Println("wrong type!")

    }

}

func main() {

    rec := rect{

        len: 1,

        wid: 2,

    }

    show("rect", rec)

    cir := circle{

        radius: 1,

    }

    show("circle", cir)

}

输出:

area of rect is 2 

perim of rect is 6 

area of circle is 3.1415927 

perim of circle is 6.2831855 

我们再来看看,保持main函数的调用顺序不变,修改一下show函数代码,

func show(name stringparam geometry) {

    switch param.(type) {

    case geometry:            //派生类型同时也属于geometry基类型

        // 类型断言

        fmt.Printf("area of %v is %v \n", name, param.(circle).area())

        fmt.Printf("perim of %v is %v \n", name, param.(circle).perim()) //这里改成了(circle).大家可以思考一下上述代码的运行结果

    default:

        fmt.Println("wrong type!")

    }

}

再次修改一下show函数代码,

func show(name stringparam interface{}) {

    switch param.(type) {

    case geometry:                                                //这里的case已经执行,其后的case并不会执行到

        // 类型断言

        fmt.Printf("[geometry]area of %v is %v \n", name, param.(geometry).area())     

        fmt.Printf("[geometry]perim of %v is %v \n", name, param.(geometry).perim())

    case circle:                                                       //并不会执行到此行代码

        fmt.Printf("[c]area of %v is %v \n", name, param.(circle).area())                          

        fmt.Printf("[c]perim of %v is %v \n", name, param.(circle).perim())

    case rect:                                                        //并不会执行到此行代码                   

        fmt.Printf("[r]area of %v is %v \n", name, param.(rect).area())                             

        fmt.Printf("[r]perim of %v is %v \n", name, param.(rect).perim())

    default:

        fmt.Println("wrong type!")

    }

}

输出:可以看到

[geometry]area of rect is 2 

[geometry]perim of rect is 6 

[geometry]area of circle is 3.1415927 

[geometry]perim of circle is 6.2831855 

再次修改一下show函数代码,

func show(name string, param interface{}) {

    switch param.(type) {

    case circle:                               //可以看到,show("circle", cir)传递进来的param.(type)是circle类型                                 

        fmt.Printf("[c]area of %v is %v \n", name, param.(circle).area())           

        fmt.Printf("[c]perim of %v is %v \n", name, param.(circle).perim())

    case rect:                                 //可以看到,show("rect", cir)传递进来的param.(type)是rect类型                             

        fmt.Printf("[r]area of %v is %v \n", name, param.(rect).area())              

        fmt.Printf("[r]perim of %v is %v \n", name, param.(rect).perim())

    default:

        fmt.Println("wrong type!")

    }

}

输出:

[r]area of rect is 2 

[r]perim of rect is 6 

[c]area of circle is 3.1415927 

[c]perim of circle is 6.2831855 

以上例子,go语言表现出了OOP中的多态的性质,下一结我们再来学习go语言中继承的性质。

小结:
go语言通过特殊的方式实现面对对象编程中的封装,继承,多态这些概念。
封装:通过方法实现
继承:通过匿名字段实现
多态:通过接口实现


发表评论

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