golang中接口的面向对象(一)--多态特征
文章从我的51cto博客搬迁过来
最近在学习golang,虽然go并不是一个传统意义的面向对象的语言,
但是发现接口interface{}这个类型却体现了一些面向对象的特点。
本人也是初学,参考了《Go 零基础编程入门教程》-- “进击的皇虫”大佬的教学。示例代码按我个人的理解稍微有所改动。
有什么不妥的地方也请大家不吝赐教。
在学习golang的面向对象之前,我们先来了解一下 方法 method 和 接口 interface{}这两个类型:
方法 method :
在Go 语言中,我们可以在一些接收者上定义函数,这里的接收者是 自定义类型或结构体,对应为OOP中的类,这些接收者的函数叫做方法。
方法(method)的声明和函数很相似, 只不过它必须指定接收者,如下代码中t1 T, t2 *T都是接收者:
<pre><code> func (t1 T) Func1(参数列表) 返回值类型{ ... } func (t2 *T) Func2(参数列表) 返回值类型{ ... } </code></pre> <ul><li>接收者的类型只能为用关键字 type 定义的类型,例如自定义类型,结构体(其实结构体也是一种自定义类型)。</li><li>同一个接收者的方法名不能重复 (没有重载),如果是结构体,方法名还不能和字段名重复。</li><li>值作为接收者无法修改其值,如果有更改需求,需要使用指针类型。</li><li>接收者不论使用某个 类型的值还是 类型的指针,其含义都是表示绑定到某该类型的方法。</li></ul>例如:
<pre><code>type T64 int64 func (a1 T64) Myfunc() { a1 = 5 } func main() { t := T64(10) t.Myfunc() fmt.Println(t) } </code></pre>输出:
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]
}
我们来看一下例子:
<pre><code>//定义了接口 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 } </code></pre>//测试
<pre><code>func show(name string, param 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) } </code></pre>输出:
area of rect is 2
perim of rect is 6
area of circle is 3.1415927
perim of circle is 6.2831855
我们再来看看,保持main函数的调用顺序不变,修改一下show函数代码,
<pre><code>func show(name string, param 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!") } } </code></pre>再次修改一下show函数代码,
<pre><code>func show(name string, param 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!") } } </code></pre>输出:可以看到
[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函数代码,
<pre><code>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!") } } </code></pre>输出:
[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
以上例子,interface{}接口类型表现出了既可能为circle又可能为rect类型的特性,从传统oop的角度而言,interface{}在这里表现得像一个基类,go语言正是通过接口类型的这种特性表现出了OOP中的多态的性质。
下一结我们再来学习go语言中继承的性质
小结:
go语言通过特殊的方式实现面对对象编程中的封装,继承,多态这些概念。
封装:通过方法实现
继承:通过匿名字段实现
多态:通过接口实现
您可能感兴趣的文章:
浅谈golang中的接口
Go核心编程-面向对象 [OOP]
golang中接口的内部实现
Go语言编程之面向“对象”编程篇
php三大特征是什么
工作好多年有可能还未真正了解接口和抽象类
Go语言基础之接口(面向对象编程下)
python是面向对象还是面向过程的
golang中接口的面向对象(一)--多态特征
Go语言中的接口