教程集 www.jiaochengji.com
教程集 >  Golang编程  >  golang教程  >  正文 GO语言接口使用指南

GO语言接口使用指南

发布时间:2022-02-24   编辑:jiaochengji.com
教程集为您提供GO语言接口使用指南等资源,欢迎您收藏本站,我们将为您提供最新的GO语言接口使用指南资源
<svg xmlns="http://www.w3.org/2000/svg" style="display: none;"><path stroke-linecap="round" d="M5,0 0,2.5 5,5z" id="raphael-marker-block" style="-webkit-tap-highlight-color: rgba(0, 0, 0, 0);"/></svg>

<h3>文章目录</h3> <ul><li><ul><li>接口</li><li>接口的定义与使用</li><li>实现的意义</li><li>类型断言</li></ul></li></ul>

<h2>接口</h2>

接口是Go语言的一种类型。简单上来讲,接口就是一系列方法的集合。通过定义接口,可以实现面向对象的多态,以及为反射提供支持。

我们可以把接口看做一个盒子,这个盒子可以装<code>类型</code> 与 <code>该类型的值</code>

<h2>
接口的定义与使用</h2>

我们知道,Go语言里面声明一个接口,或者类型,有3种方式

<ul><li>定义型:<code>type Doer interface { Do() }</code></li><li>非定义型 <code>interface { Do() }</code></li><li>非定义型别名 <code>type Doer = interface { Do() }</code></li></ul>

别名方式在编译器看来,和原始类型是同一类型,相互可以赋值

下面我们来看看如何实现一个接口呢

<pre><code class="lang-go hljs"><span class="token keyword">type</span> MyInt <span class="token builtin">int</span> <span class="token keyword">func</span> <span class="token punctuation">(</span><span class="token operator">*</span>MyInt<span class="token punctuation">)</span> <span class="token function">Do</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token punctuation">{</span> fmt<span class="token punctuation">.</span><span class="token function">Println</span><span class="token punctuation">(</span><span class="token string">"i am do "</span><span class="token punctuation">)</span> <span class="token punctuation">}</span> </code></pre>

这样就实现了上面定义的那个接口了,这是一个指针接收者,可以定义一个<code>MyInt</code>变量去调用 <code>Do</code> 方法,下面列举了定义该类型和使用该类型的方法

<pre><code class="lang-go hljs"><span class="token keyword">func</span> <span class="token function">main</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token punctuation">{</span> a <span class="token operator">:=</span> <span class="token function">new</span><span class="token punctuation">(</span>MyInt<span class="token punctuation">)</span> b <span class="token operator">:=</span> <span class="token function">MyInt</span><span class="token punctuation">(</span><span class="token number">1</span><span class="token punctuation">)</span> <span class="token keyword">var</span> c MyInt <span class="token keyword">var</span> d MyInt <span class="token operator">=</span> <span class="token number">1</span> <span class="token keyword">var</span> e Doer <span class="token operator">=</span> <span class="token function">new</span><span class="token punctuation">(</span>MyInt<span class="token punctuation">)</span> <span class="token keyword">var</span> f Doer <span class="token operator">=</span> <span class="token function">MyInt</span><span class="token punctuation">(</span><span class="token number">1</span><span class="token punctuation">)</span> <span class="token comment">// 编译出错,因为Do方法为指针接收,所以该接口变量无法接收一个值类型的值,只能接收指针类型的值</span> a<span class="token punctuation">.</span><span class="token function">do</span><span class="token punctuation">(</span><span class="token punctuation">)</span> b<span class="token punctuation">.</span><span class="token function">do</span><span class="token punctuation">(</span><span class="token punctuation">)</span> c<span class="token punctuation">.</span><span class="token function">do</span><span class="token punctuation">(</span><span class="token punctuation">)</span> d<span class="token punctuation">.</span><span class="token function">do</span><span class="token punctuation">(</span><span class="token punctuation">)</span> e<span class="token punctuation">.</span><span class="token function">do</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token punctuation">}</span> </code></pre>

除了使用指针接收方式,还可以使用值接收方式,比如像这样

<pre><code class="lang-go hljs"><span class="token keyword">func</span> <span class="token punctuation">(</span>MyInt<span class="token punctuation">)</span> <span class="token function">Do</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token punctuation">{</span> fmt<span class="token punctuation">.</span><span class="token function">Println</span><span class="token punctuation">(</span><span class="token string">"i am do "</span><span class="token punctuation">)</span> <span class="token punctuation">}</span> </code></pre>

那么这两种有何区别呢,只需要记住,「指针接收」比「值接收」更严苛,区别就在于,使用「接口变量」能包裹的值的类型是<code>值</code>还是<code>指针</code>,所以上述定义为指针接收类型的方法的时候,接口变量就不能包裹值类型,所以上述<code>f</code>变量那行会出现编译错误

说得通俗点,定义为<code>值接收者</code> 方法时,指针和值都可以调用,我们可以通过下面这两段代码测试一下

<pre><code class="lang-go hljs"><span class="token keyword">package</span> main <span class="token keyword">import</span> <span class="token punctuation">(</span> <span class="token string">"fmt"</span> <span class="token string">"reflect"</span> <span class="token punctuation">)</span> <span class="token keyword">type</span> A <span class="token keyword">struct</span> <span class="token punctuation">{</span> <span class="token punctuation">}</span> <span class="token keyword">func</span> <span class="token punctuation">(</span><span class="token operator">*</span>A<span class="token punctuation">)</span> <span class="token function">Do</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token punctuation">{</span> fmt<span class="token punctuation">.</span><span class="token function">Println</span><span class="token punctuation">(</span><span class="token string">"call A do"</span><span class="token punctuation">)</span> <span class="token punctuation">}</span> <span class="token keyword">type</span> B <span class="token keyword">struct</span> <span class="token punctuation">{</span> <span class="token punctuation">}</span> <span class="token keyword">func</span> <span class="token punctuation">(</span>B<span class="token punctuation">)</span> <span class="token function">Do</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token punctuation">{</span> fmt<span class="token punctuation">.</span><span class="token function">Println</span><span class="token punctuation">(</span><span class="token string">"call B do"</span><span class="token punctuation">)</span> <span class="token punctuation">}</span> <span class="token keyword">func</span> <span class="token function">main</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token punctuation">{</span> <span class="token comment">//A{}.Do() // 编译有错,无法使用值去调用指针接收者的方法</span> <span class="token function">new</span><span class="token punctuation">(</span>A<span class="token punctuation">)</span><span class="token punctuation">.</span><span class="token function">Do</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token comment">// 正确</span> B<span class="token punctuation">{</span><span class="token punctuation">}</span><span class="token punctuation">.</span><span class="token function">Do</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token comment">// 值接收者时候,值和指针都能正确调用</span> <span class="token function">new</span><span class="token punctuation">(</span>B<span class="token punctuation">)</span><span class="token punctuation">.</span><span class="token function">Do</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token comment">//</span> <span class="token function">printMethodList</span><span class="token punctuation">(</span>A<span class="token punctuation">{</span><span class="token punctuation">}</span><span class="token punctuation">)</span> <span class="token comment">// output: 无打印</span> <span class="token function">printMethodList</span><span class="token punctuation">(</span><span class="token operator">&</span>A<span class="token punctuation">{</span><span class="token punctuation">}</span><span class="token punctuation">)</span> <span class="token comment">// output: Method Name is Do()</span> <span class="token function">printMethodList</span><span class="token punctuation">(</span>B<span class="token punctuation">{</span><span class="token punctuation">}</span><span class="token punctuation">)</span> <span class="token comment">// output: Method Name is Do()</span> <span class="token function">printMethodList</span><span class="token punctuation">(</span><span class="token operator">&</span>B<span class="token punctuation">{</span><span class="token punctuation">}</span><span class="token punctuation">)</span> <span class="token comment">// output: Method Name is Do()</span> <span class="token punctuation">}</span> <span class="token comment">// 打印p拥有的方法集</span> <span class="token keyword">func</span> <span class="token function">printMethodList</span><span class="token punctuation">(</span>p <span class="token keyword">interface</span><span class="token punctuation">{</span><span class="token punctuation">}</span><span class="token punctuation">)</span> <span class="token punctuation">{</span> v <span class="token operator">:=</span> reflect<span class="token punctuation">.</span><span class="token function">TypeOf</span><span class="token punctuation">(</span>p<span class="token punctuation">)</span> <span class="token keyword">for</span> i <span class="token operator">:=</span> <span class="token number">0</span><span class="token punctuation">;</span> i <span class="token operator"><</span> v<span class="token punctuation">.</span><span class="token function">NumMethod</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span> i<span class="token operator"> </span> <span class="token punctuation">{</span> fmt<span class="token punctuation">.</span><span class="token function">Println</span><span class="token punctuation">(</span><span class="token string">"Method Name is "</span> <span class="token operator"> </span> v<span class="token punctuation">.</span><span class="token function">Method</span><span class="token punctuation">(</span>i<span class="token punctuation">)</span><span class="token punctuation">.</span>Name <span class="token operator"> </span> <span class="token string">"()"</span><span class="token punctuation">)</span> <span class="token punctuation">}</span> <span class="token punctuation">}</span> </code></pre>

在使用<code>值接收</code> 定义方法时候,go编译器替我们省略了一步

<pre><code class="lang-go hljs"><span class="token comment">// new(B).Do() 为啥可以正确打印呢?这一步go编译器会做下面两步,属于一个语法糖</span> p <span class="token operator">:=</span> <span class="token function">new</span><span class="token punctuation">(</span>B<span class="token punctuation">)</span> <span class="token punctuation">(</span><span class="token operator">*</span>p<span class="token punctuation">)</span><span class="token punctuation">.</span>DO </code></pre>

那么值接收和指针接收,除了以上说的那些,还有什么区别呢?

指针接收,指向的都是调用该方法的对象,对象只有一份;

值接收,是对源对象的拷贝,也就是说,每调用一个改对象的方法,则对其进行一次拷贝

那么什么时候使用<code>值接收</code>, 什么时候使用<code>指针接收呢</code>

推荐使用值接收的情况:

<ul><li>内置类型(int string) 等</li><li>slice,map,interface,channel 这些内置结构体类型</li></ul>

在使用 比如 map 这种内置结构体类型的时候,拷贝是只会拷贝其 header,相当于共享一份底层

推荐使用指针接收的情况:

<ul><li>自定义结构体类型</li></ul><h2>
实现的意义</h2>

首先,一个类型,它拥有的所有方法的集合,称之为方法集。

如果这个方法集是某个接口的「超集」,我们就说这个类型实现了该接口,于是这个类型的值可以赋值给该接口变量。

所有类型都是「空接口」的实现,所以任意类型都能赋值给空接口变量

由于Go中的实现关系是隐式的,所以如果你声明了一个接口,某些类型可能「被动」地实现了你这个接口的方法,于是就可以将该类型的实例,赋值给此接口变量。

Go里所有的类型都实现了 <code>interface{}</code> 这个空接口,所以可以将任何值赋值给空接口变量,比如下面这段代码

<pre><code class="lang-go hljs"><span class="token keyword">var</span> i <span class="token keyword">interface</span><span class="token punctuation">{</span><span class="token punctuation">}</span> <span class="token operator">=</span> <span class="token number">123</span> </code></pre>

有一个很重要的思想方法就是,当你要赋值给一个接口变量时,你这个类型只要包含了接口所定义的方法集,那么就可以赋值给它

<pre><code class="lang-go hljs"><span class="token keyword">func</span> <span class="token function">Add</span><span class="token punctuation">(</span>a<span class="token punctuation">,</span> b <span class="token keyword">interface</span><span class="token punctuation">{</span><span class="token punctuation">}</span><span class="token punctuation">)</span> <span class="token punctuation">{</span> <span class="token keyword">if</span> aa<span class="token punctuation">,</span> ok <span class="token operator">:=</span> a<span class="token punctuation">.</span><span class="token punctuation">(</span><span class="token keyword">interface</span><span class="token punctuation">{</span><span class="token function">Do</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">}</span><span class="token punctuation">)</span><span class="token punctuation">;</span> ok <span class="token punctuation">{</span> <span class="token comment">// 只要 a 这个类型有 Do 方法,则这里就可以编译通过</span> aa<span class="token punctuation">.</span><span class="token function">Do</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token comment">// ...</span> <span class="token punctuation">}</span> <span class="token punctuation">}</span> </code></pre> <h2>
类型断言</h2>

其实在Go语言里面,类型的断言,就类似于Java里的类型强转差不多(使用场景),目的是,将通用型接口变量,转向更定制化的方向。下面举个例子,不过有一点必须记住,::被断言的一定是接口类型::

<pre><code class="lang-go hljs"><span class="token keyword">type</span> Sayer <span class="token keyword">interface</span> <span class="token punctuation">{</span> <span class="token function">Say</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token punctuation">}</span> <span class="token keyword">type</span> People <span class="token keyword">struct</span> <span class="token punctuation">{</span> Name <span class="token builtin">string</span> <span class="token punctuation">}</span> <span class="token keyword">func</span> <span class="token punctuation">(</span><span class="token operator">*</span>People<span class="token punctuation">)</span> <span class="token function">Say</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token punctuation">{</span> fmt<span class="token punctuation">.</span><span class="token function">Println</span><span class="token punctuation">(</span><span class="token string">"我是一个人,我正在说话"</span><span class="token punctuation">)</span> <span class="token punctuation">}</span> <span class="token keyword">func</span> <span class="token function">main</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token punctuation">{</span> <span class="token keyword">var</span> sayer Sayer <span class="token operator">=</span> <span class="token operator">&</span>People<span class="token punctuation">{</span><span class="token punctuation">}</span> <span class="token comment">// 因为 People 实现了 Sayer接口,所以可以赋值给该接口的变量</span> sayer<span class="token punctuation">.</span><span class="token function">Say</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token comment">// 调用该接口方法,此时并不管关心具体实现方式是怎样的,只要实现了就行</span> people <span class="token operator">:=</span> sayer<span class="token punctuation">.</span><span class="token punctuation">(</span><span class="token operator">*</span>People<span class="token punctuation">)</span> <span class="token comment">// 此时你很肯定此接口变量包裹的就是 *People 类型,所以不需要第二个参数进行判断</span> <span class="token punctuation">}</span> </code></pre>

上面简单使用了一下类型断言,它的表达式有2种

<pre><code class="lang-go hljs">v1 <span class="token operator">:=</span> i<span class="token punctuation">.</span><span class="token punctuation">(</span>Type<span class="token punctuation">)</span> <span class="token comment">// 确定就是这个类型的时候,无需第二个参数进行判断</span> v2<span class="token punctuation">,</span> ok <span class="token operator">:=</span> i<span class="token punctuation">.</span><span class="token punctuation">(</span>Type<span class="token punctuation">)</span> <span class="token comment">// 无法确定它的类型,ok 是个 bool 值,可以通过它判断是否实现</span> </code></pre>

假如使用了第一种,类型又断言错了的话,会直接产生一个 panic,用第二种则不会,可以通过 ok 值来进行判断是否实现了该接口

有时候新手肯定会纳闷,啥时候该断言呢?我的接口类型是该放外面,还是括号里面呢。

其实只要记住,断言的目的。

咱们平时说「断言」二字,其实就是就算断定的意思,你很断定这个接口的动态类型,就是某个具体的类型。

断言的时候,可以不断言出具体的类型,也就是断言的类型可以是接口 <code>i.(另一个接口类型B)</code> ,但是 i 和 此接口类型,一定是可以装载同一个动态类型。i 是比较通用的接口类型,而 i 中的动态类型,一定也是实现了 接口B,只不过此时我们只需要使用 接口B 的方法,所以没必要将整个动态类型断言出来(当然断言出整个动态类型也没什么错)

如果还是懵逼,可以记住下面的公式

<pre><code class="lang-go hljs">更通用的接口<span class="token punctuation">.</span><span class="token punctuation">(</span>较为定制的类型<span class="token punctuation">)</span> </code></pre>

左边的一定是更加通用的,右边一定是更加实现的:通用 -> 实现

记住这个公式,可以避免犯错,百试不爽

[未完待续]。。。

到此这篇关于“GO语言接口使用指南”的文章就介绍到这了,更多文章或继续浏览下面的相关文章,希望大家以后多多支持JQ教程网!

您可能感兴趣的文章:
想系统学习GO语言(Golang
Go 开发关键技术指南 | 为什么你要选择 Go?(内含超全知识大图)
关于golang面向接口
Go 语言一本通
Go语言空接口类型(interface{})
Go 语言到底适合干什么?
初识 go 语言:方法,接口及并发
想学一门新的编程语言?考虑一下Go (Golang)吧
go 获取函数地址_Go语言基础--接口浅析
Go语言接口interface

[关闭]
~ ~