《Go语言学习笔记》 - 第七章 - 接口
在<code>Go</code>语言中,接口是一组方法的签名。当某个类型为接口中的所有方法提供了方法的实现,它就实现了该接口。在<code>Go</code>语言中,接口和实现类的关系是非侵入式,也就是说一个类型要实现接口的话没必要显式的去声明自己实现了该接口,只要默默的将接口中的方法实现即可。
接口区别于我们之前所有的具体类型,接口是一种抽象的类型。当你看到一个接口类型的值时,你不知道它是什么,唯一知道的是通过它的方法能做什么。
<h3>语法</h3>接口的定义:
<pre><code class="lang-go hljs"><span class="token keyword">type</span> 接口名 <span class="token keyword">interface</span><span class="token punctuation">{</span> 方法名<span class="token punctuation">(</span>参数列表<span class="token punctuation">)</span>返回值 <span class="token operator">...</span> <span class="token punctuation">}</span> </code></pre>如果接口没有任何的方法声明,那么它就是一个空接口,它的用途类似于面向对象里的根类型<code>Object</code>,可被赋值为任何类型的对象。
接口默认值:
接口的默认值是<code>nil</code>,如果实现接口的类型支持的话,可以做相等运算。
接口的实现:
一个对象只要全部实现了接口中的方法,那么就实现了这个接口。换句话说,接口就是一个需要实现的方法列表。
构建一个<code>Action</code>接口,创建一个<code>Student</code>类,然后实现<code>Action</code>接口。
要实现一个接口很简单,只需要实现接口中声明的方法即可,那我们实现了接口之后有啥用呢?
让我们想象一下这样的场景,你创建了两个对象,一个叫<code>支付宝</code>,一个叫<code>微信</code>,它们都具有<code>付款</code>这一个功能。现在你要写一个<code>收款函数</code>,这个函数能够对传入的对象进行收款操作,但是现在有一个问题,你不知道传入的对象到底是支付宝还是微信,所以可能要先判断再进行相应的收款操作。当对象较少时可以判断,如果对象很多怎么办呢?
这个时候接口就能排上用场了,接口是一种抽象的类型。当你看到一个接口类型的值时,你不知道它是什么,唯一知道的是通过它的方法能做什么。
因此,我们可以定义一个<code>支付方式</code>的接口,这个接口中声明了一个<code>支付方法</code>,然后分别用<code>支付宝</code>和<code>微信</code>这两个对象去实现接口中所声明的方法。紧接着把<code>收款函数</code>的传入参数类型设置为接口类型即可。
<pre><code class="lang-go hljs"> <span class="token keyword">type</span> AliPay <span class="token keyword">struct</span> <span class="token punctuation">{</span> <span class="token punctuation">}</span> <span class="token keyword">type</span> WeChat <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>AliPay<span class="token punctuation">)</span> <span class="token function">Pay</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 punctuation">(</span>WeChat<span class="token punctuation">)</span> <span class="token function">Pay</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">type</span> PayMethod <span class="token keyword">interface</span> <span class="token punctuation">{</span> <span class="token function">Pay</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token punctuation">}</span> <span class="token keyword">func</span> <span class="token function">GetMoney</span><span class="token punctuation">(</span>p PayMethod<span class="token punctuation">)</span> <span class="token punctuation">{</span> p<span class="token punctuation">.</span><span class="token function">Pay</span><span class="token punctuation">(</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> a <span class="token operator">:=</span> AliPay<span class="token punctuation">{</span><span class="token punctuation">}</span> w <span class="token operator">:=</span> WeChat<span class="token punctuation">{</span><span class="token punctuation">}</span> <span class="token function">GetMoney</span><span class="token punctuation">(</span>a<span class="token punctuation">)</span> <span class="token function">GetMoney</span><span class="token punctuation">(</span>w<span class="token punctuation">)</span> <span class="token punctuation">}</span> </code></pre>输出结果为:
<pre><code class="lang-go hljs">支付宝转账 微信转账 </code></pre> <h3>值接收者和指针接收者实现接口的区别</h3> <pre><code class="lang-go hljs"><span class="token keyword">type</span> Mover <span class="token keyword">interface</span> <span class="token punctuation">{</span> <span class="token function">move</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token punctuation">}</span> <span class="token keyword">type</span> dog <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>d dog<span class="token punctuation">)</span> <span class="token function">move</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> x Mover <span class="token keyword">var</span> wangcai <span class="token operator">=</span> dog<span class="token punctuation">{</span><span class="token punctuation">}</span> <span class="token comment">// 旺财是dog类型</span> x <span class="token operator">=</span> wangcai <span class="token comment">// x可以接收dog类型</span> <span class="token keyword">var</span> fugui <span class="token operator">=</span> <span class="token operator">&</span>dog<span class="token punctuation">{</span><span class="token punctuation">}</span> <span class="token comment">// 富贵是*dog类型</span> x <span class="token operator">=</span> fugui <span class="token comment">// x可以接收*dog类型</span> x<span class="token punctuation">.</span><span class="token function">move</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token punctuation">}</span> </code></pre>从上面的代码中我们可以发现,使用值接收者实现接口之后,不管是<code>dog</code>结构体还是结构体指针<code>*dog</code>类型的变量都可以赋值给该接口变量。因为<code>Go</code>语言中有对指针类型变量求值的语法糖,<code>dog</code>指针<code>fugui</code>内部会自动求值<code>*fugui</code>。
同样的代码我们再来测试一下使用指针接收者有什么区别:
<pre><code class="lang-go hljs"><span class="token keyword">func</span> <span class="token punctuation">(</span>d <span class="token operator">*</span>dog<span class="token punctuation">)</span> <span class="token function">move</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> x Mover <span class="token keyword">var</span> wangcai <span class="token operator">=</span> dog<span class="token punctuation">{</span><span class="token punctuation">}</span> <span class="token comment">// 旺财是dog类型</span> x <span class="token operator">=</span> wangcai <span class="token comment">// x不可以接收dog类型</span> <span class="token keyword">var</span> fugui <span class="token operator">=</span> <span class="token operator">&</span>dog<span class="token punctuation">{</span><span class="token punctuation">}</span> <span class="token comment">// 富贵是*dog类型</span> x <span class="token operator">=</span> fugui <span class="token comment">// x可以接收*dog类型</span> <span class="token punctuation">}</span> </code></pre>此时实现<code>Mover</code>接口的是<code>*dog</code>类型,所以不能给<code>x</code>传入<code>dog</code>类型的<code>wangcai</code>,此时<code>x</code>只能存储<code>*dog</code>类型的值。
<h3>接口的嵌套</h3>可以像匿名字段一样,嵌入其他接口。目标类型方法集中,必须有包含嵌入接口方法在内的全部方法才算实现了该接口。
嵌入其他的接口类型,这就相当于将其声明的方法集导入。这就要求不能有重名方法,因为不支持重载。还有,不能嵌入自身或者循环嵌入,那会导致递归错误。
<pre><code class="lang-go hljs"><span class="token keyword">type</span> stringer <span class="token keyword">interface</span> <span class="token punctuation">{</span> <span class="token function">string</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token builtin">string</span> <span class="token punctuation">}</span> <span class="token keyword">type</span> tester <span class="token keyword">interface</span> <span class="token punctuation">{</span> stringer <span class="token function">test</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token punctuation">}</span> <span class="token keyword">type</span> data <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>data<span class="token punctuation">)</span> <span class="token function">test</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">func</span> <span class="token punctuation">(</span>data<span class="token punctuation">)</span> <span class="token function">string</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token builtin">string</span> <span class="token punctuation">{</span> <span class="token keyword">return</span> <span class="token string">""</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> d data <span class="token keyword">var</span> t tester <span class="token operator">=</span> <span class="token operator">&</span>d t<span class="token punctuation">.</span><span class="token function">test</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token function">println</span><span class="token punctuation">(</span>t<span class="token punctuation">.</span><span class="token function">string</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">)</span> <span class="token punctuation">}</span> </code></pre> <h3>接口的执行机制</h3>接口结构如下:
<pre><code class="lang-go hljs"><span class="token keyword">type</span> iface <span class="token keyword">struct</span> <span class="token punctuation">{</span> tab <span class="token operator">*</span>itab data unsafe<span class="token punctuation">.</span>Pointer <span class="token punctuation">}</span> </code></pre>其中<code>itab</code>存储了运行期所需的相关类型信息。
<pre><code class="lang-go hljs"><span class="token keyword">type</span> itab <span class="token keyword">struct</span><span class="token punctuation">{</span> inter <span class="token operator">*</span><span class="token keyword">interface</span> <span class="token comment">// 接口类型</span> _type <span class="token operator">*</span>_type <span class="token comment">// 实际对象类型</span> fun <span class="token punctuation">[</span><span class="token number">1</span><span class="token punctuation">]</span><span class="token builtin">uintptr</span> <span class="token comment">// 实际对象方法地址</span> <span class="token punctuation">}</span> </code></pre><code>itab</code>里面保存了接口和实际对象的元数据,同时,<code>itab</code>还用<code>fun</code>数组保存了实际方法地址,从而实现在运行期间对目标方法的动态调用。
除此之外,接口还有一个重要特征:将对象赋值给接口时,会复制该对象。
在接口内部,只有当两个指针(<code>itab</code>和<code>data</code>)都为<code>nil</code>时,接口才等于<code>nil</code>。
到此这篇关于“《Go语言学习笔记》 - 第七章 - 接口”的文章就介绍到这了,更多文章或继续浏览下面的相关文章,希望大家以后多多支持JQ教程网!您可能感兴趣的文章:
想系统学习GO语言(Golang
go run main.go 参数_Go语言入门:Hello world
go 语言学习历程
Go 语言十年而立,Go2 蓄势待发
go语言学习笔记(第3章)—面向对象编程
Golang学习笔记(五):Go语言与C语言的区别
Go语言发展历史、核心、特性及学习路线
Go 语言数据类型:byte、rune与字符串
Golang笔记:语法,并发思想,web开发,Go微服务相关
Go 语言进阶教程