go接口详解
<p> </p><p>go面向接口编程知识点</p>
<ul>
<li>接口定义与格式</li>
<li>隐式实现及实现条件</li>
<li>接口赋值</li>
<li>空接口</li>
<li>接口嵌套</li>
<li>类型断言</li>
<li>多态</li>
</ul>
<p> </p>
<h3>接口定义与格式</h3>
<p>接口(interface)是一种类型,用来定义行为(方法)。这句话有两个重点,<strong>类型</strong>和<strong>定义行为</strong>。</p>
<p>首先解释定义行为:<br>接口即一组方法定义的集合,定义了对象的一组行为,就是定义了一些函数,由具体的类型实例实现具体的方法。<br>换句话说,一个接口就是定义(规范或约束),接口并不会实现这些方法,具体的实现由类实现,实现接口的类必须严格按照接口的声明来实现接口提供的所有功能。接口的作用应该是将定义与实现分离,降低耦合度。<br>在多人合作开发同一个项目时,接口表示调用者和设计者的一种约定,事先定义好相互调用的接口可以大大提高开发的效率。有了接口,就可以在不影响现有接口声明的情况下,修改接口的内部实现,从而使兼容性问题最小化。</p>
<p>接口的定义格式:</p>
<div class="cnblogs_Highlighter">
<pre class="brush:go;gutter:true;">type Namer interface {
Method1(param_list) return_type//方法名(参数列表) 返回值列表
Method2(param_list) return_type//方法名(参数列表) 返回值列表<br> ......</pre>
<pre class="brush:go;gutter:true;"> }</pre>
</div>
<p> </p>
<h3>隐式实现及实现条件</h3>
<p><strong>怎么实现接口:</strong><br>实现接口的类并不需要显式声明,只需要实现接口所有的函数就表示实现了该接口,而且类还可以拥有自己的方法。</p>
<p><strong>接口能被哪些类型实现:</strong><br>接口可以被结构体实现,也可以被函数类型实现。<br><strong>接口被实现的条件:</strong><br>接口被实现的条件一:接口的方法与实现接口的类型方法格式一致(方法名、参数类型、返回值类型一致)。<br>接口被实现的条件二:接口中所有方法均被实现。</p>
<div class="cnblogs_Highlighter">
<pre class="brush:go;gutter:true;">package main
import "fmt"
type Shaper interface {
Area() float64
//Perimeter() float64
}
type Rectangle struct {
length float64
widthfloat64
}
// 实现 Shaper 接口中的方法
func (r *Rectangle) Area() float64 {
return r.length * r.width
}
// Set 是属于 Rectangle 自己的方法
func (r *Rectangle) Set(l float64, w float64) {
r.length = l
r.width = w
}
func main() {
rect := new(Rectangle) //创建指针类型的结构体实例(类实例)
rect.Set(2, 3)
areaIntf := Shaper(rect) //这里将指针类型实例赋值给接口,下面会介绍。
fmt.Printf("The rect has area: %f\n", areaIntf.Area())
}
</pre>
</div>
<p> </p>
<h3>接口赋值</h3>
<p>现在来解释接口是一个类型,本质是一个指针类型,那么什么样的值可以赋值给接口,有两种:<strong>实现了该接口的类</strong>或者<strong>接口</strong>。</p>
<p><strong>1.将对象赋值给接口</strong><br>当接口实例中保存了自定义类型的实例后,就可以直接从接口上调用它所保存的实例的方法。</p>
<div class="cnblogs_Highlighter">
<pre class="brush:go;gutter:true;">package main
import (
"fmt"
)
//定义接口
type Testinterface interface{
Teststring() string
Testint() int
}
//定义结构体
type TestMethod struct{
name string
age int
}
//结构体的两个方法隐式实现接口
func (t *TestMethod)Teststring() string{
return t.name
}
func (t *TestMethod)Testint() int{
return t.age
}
func main(){
T1 := &TestMethod{"ling",34}
T2 := TestMethod{"gos",43}
//接口本质是一种类型
//接口赋值:只要类实现了该接口的所有方法,即可将该类赋值给这个接口
var Test1 Testinterface//接口只能是值类型
Test1 = T1 //TestMethod类的指针类型实例传给接口
fmt.Println(Test1.Teststring())
fmt.Println(Test1.Testint())
Test2 := T2 //TestMethod类的值类型实例传给接口
fmt.Println(Test2.Teststring())
fmt.Println(Test2.Testint())
}</pre>
</div>
<p><strong>2.将接口赋值给另一个接口</strong></p>
<p>1.只要两个接口拥有相同的方法列表(与次序无关),即是两个相同的接口,可以相互赋值<br>2.接口赋值只需要接口A的方法列表是接口B的子集(即假设接口A中定义的所有方法,都在接口B中有定义),那么B接口的实例可以赋值给A的对象。反之不成立,即子接口B包含了父接口A,因此可以将子接口的实例赋值给父接口。<br>3.即子接口实例实现了子接口的所有方法,而父接口的方法列表是子接口的子集,则子接口实例自然实现了父接口的所有方法,因此可以将子接口实例赋值给父接口。</p>
<p><strong> 3.接口类型作为参数</strong></p>
<p>第一点已经说了可以将实现接口的类赋值给接口,而将接口类型作为参数很常见。这时,那些实现接口的实例都能作为接口类型参数传递给函数/方法。</p>
<div class="cnblogs_Highlighter">
<pre class="brush:go;gutter:true;">package main
import (
"fmt"
)
//Shaper接口
type Shaper interface {
Area() float64
}
// Circle struct结构体
type Circle struct {
radius float64
}
// Circle类型实现Shaper中的方法Area()
func (c *Circle) Area() float64 {
return 3.14 * c.radius * c.radius
}
func main() {
// Circle的指针类型实例
c1 := new(Circle)
c1.radius = 2.5
//将 Circle的指针类型实例c1传给函数myArea,接收类型为Shaper接口
myArea(c1)
}
func myArea(n Shaper) {
fmt.Println(n.Area())
}
</pre>
</div>
<p> </p>
<h3>空接口</h3>
<p>空接口是指没有定义任何接口方法的接口。<strong>没有定义任何接口方法,意味着Go中的任意对象都已经实现空接口(因为没方法需要实现),只要实现接口的对象都可以被接口保存,所以任意对象都可以保存到空接口实例变量中</strong>。</p>
<p>空接口的定义方式:</p>
<div class="cnblogs_Highlighter">
<pre class="brush:go;gutter:true;">type empty_int interface {}</pre>
</div>
<p>更常见的,会直接使用<code>interface{}</code>作为一种类型,表示空接口。例如:</p>
<div class="cnblogs_Highlighter">
<pre class="brush:go;gutter:true;">// 声明一个空接口实例
var i interface{}</pre>
</div>
<p>再比如函数使用空接口类型参数:</p>
<div class="cnblogs_Highlighter">
<pre class="brush:go;gutter:true;">func myfunc(i interface{})</pre>
</div>
<p>如何使用空接口</p>
<p>可以定义一个空接口类型的array、slice、map、struct等,这样它们就可以用来存放任意类型的对象,因为任意类型都实现了空接口。</p>
<div class="cnblogs_Highlighter">
<pre class="brush:go;gutter:true;">package main
import "fmt"
func main() {
any := make([]interface{}, 5)
any = 11
any = "hello world"
any = []int{11, 22, 33, 44}
for _, value := range any {
fmt.Println(value)
}
}
</pre>
</div>
<div class="cnblogs_Highlighter">
<pre class="brush:go;gutter:true;">11
hello world
<nil>
<nil>
</pre>
</div>
<p>通过空接口类型,Go也能像其它动态语言一样,在数据结构中存储任意类型的数据。</p>
<p> </p>
<h3>接口嵌套</h3>
<p>接口可以嵌套,嵌套的内部接口将属于外部接口,内部接口的方法也将属于外部接口。</p>
<p>另外在类型嵌套时,如果内部类型实现了接口,那么外部类型也会自动实现接口,因为内部属性是属于外部属性的。</p>
<div class="cnblogs_Highlighter">
<pre class="brush:go;gutter:true;">type ReadWrite interface {
Read(b Buffer) bool
Write(b Buffer) bool
}
type Lock interface {
Lock()
Unlock()
}
type File interface {<br> //ReadWrite为内部接口
ReadWrite<br> //Lock为内部接口
Lock
Close()
}
</pre>
</div>
<p> </p>
<h3> 类型断言</h3>
<p>类型断言为判断一个类型有没有实现接口。</p>
<p>假如我现在写了一个结构体类型 <code>MyFile</code> 来实现上面的 <code>File</code> 接口,那么我如何知道 <code>MyFile</code> 是否实现了 <code>File</code> 接口呢?</p>
<div class="cnblogs_Highlighter">
<pre class="brush:go;gutter:true;">package main
import "fmt"
type MyFile struct{}
func (m *MyFile) Read() bool {
fmt.Printf("Read()\n")
return true
}
// ...
// 假设我这里相继实现了 Write(), Lock(),Unlock() 和 Close() 方法
func main() {
my := new(MyFile)
fIntf := File(my)
// 看这里,看这里
if v, ok := fIntf.(*MyFile); ok {
v.Read()
}
} </pre>
</div>
<p>类型断言的格式:</p>
<div class="cnblogs_Highlighter">
<pre class="brush:go;gutter:true;">if v, ok : = varI.(T) ; ok { <br> // checked type assertion<br> //do something
return
}
</pre>
</div>
<p>如果 <code>v</code> 是 <code>varI</code> 转换到类型 <code>T</code> 的值,<code>ok</code> 会是 <code>true</code>;否则 <code>v</code> 是类型 <code>T</code> 的零值,<code>ok</code> 是 <code>false</code>。 </p>
<p>要是多个类型实现了同一个接口,比如前面的 <code>areaIntf</code>,要如何测试呢? <br>那就要用 <code>type-switch</code> 来判断了。</p>
<div class="cnblogs_Highlighter">
<pre class="brush:go;gutter:true;">switch t := areaIntf.(type) {
case *Rectangle:
// do something
case *Triangle:
// do something
default:
// do something
}
</pre>
</div>
<p> </p>
<h3>多态</h3>
<p>1、多个类型(结构体)可以实现同一个接口。 <br>2、一个类型(结构体)可以实现多个接口。<br>3、实现接口的类(结构体)可以赋值给接口。</p>
<div class="cnblogs_Highlighter">
<pre class="brush:go;gutter:true;">package main
import "fmt"
type Shaper interface {
Area() float64
}
// ==== Rectangle ====
type Rectangle struct {
length float64
widthfloat64
}
// 实现 Shaper 接口中的方法
func (r *Rectangle) Area() float64 {
return r.length * r.width
}
// Set 是属于 Rectangle 自己的方法
func (r *Rectangle) Set(l float64, w float64) {
r.length = l
r.width = w
}
// ==== Triangle ====
type Triangle struct {
bottom float64
hightfloat64
}
func (t *Triangle) Area() float64 {
return t.bottom * t.hight / 2
}
func (t *Triangle) Set(b float64, h float64) {
t.bottom = b
t.hight = h
}
// ==== Triangle End ====
func main() {
rect := new(Rectangle)
rect.Set(2, 3)
areaIntf := Shaper(rect) //这种方法只能将指针类型的类示例赋值给接口
fmt.Printf("The rect has area: %f\n", areaIntf.Area())
triangle := new(Triangle)
triangle.Set(2, 3)
areaIntf = Shaper(triangle) //这种方法只能将指针类型的类示例赋值给接口
fmt.Printf("The triangle has area: %f\n", areaIntf.Area())
}
</pre>
</div>
<p> </p><br><br>
来源:https://www.cnblogs.com/-wenli/p/12316479.html
頁:
[1]