|
|
51CTO旗下网站
|
|
移动端

9.1.5 接口赋值

《Go语言编程入门与实战技巧》第9章接口与反射,本章需要注意设计类型时确认类型的本质是原始的还是非原始的。接口是声明了一组行为并支持多态的类型,嵌入类型提供了扩展类型的能力,而无须使用继承。本节为大家介绍接口赋值。

作者:黄靖钧来源:电子工业出版社|2018-09-23 09:26

9.1.5  接口赋值

接口赋值在Go语言中分为如下两种情况:将对象实例赋值给接口;将一个接口赋值给另一个接口。

先讨论将某种类型的对象实例赋值给接口,这要求该对象实例实现了接口要求的所有方法,如下:

  1. type Integer int  
  2.  
  3. func (a Integer) Less(b Integer) bool {  
  4.     return a < b 
  5. }  
  6. func (a *Integer) Add(b Integer) {  
  7.     *a += b  
  8. }  

相应地,定义接口LessAdder:

  1. type LessAdder interface {  
  2.     Less(b Integer) bool  
  3.     Add(b Integer)  
  4. }  

现在有个问题:假设定义一个Integer类型的对象实例,怎样将其赋值给LessAdder接口呢?应该用下面的语句1,还是语句2呢?

  1. var a Integer = 1 
  2. var b LessAdder = &a    // 语句1  
  3. var b LessAdder = a // 语句2  

答案是应该用语句1,原因在于,Go语言可以根据函数func (a Integer) Less(b Integer) bool自动生成一个新的Less()方法:

  1. func (a *Integer) Less(b Integer) bool {  
  2.     return (*a).Less(b)  
  3. }  

这样,类型*Integer就既包含Less()方法,也包含Add()方法,满足LessAdder接口。而从另一方面来说,根据func (a *Integer) Add(b Integer)函数无法自动生成以下这个成员方法:

  1. func (a Integer) Add(b Integer) {  
  2.     (&a).Add(b)  
  3. }  

因为(&a).Add()改变的只是函数参数a,对外部实际要操作的对象并无影响,这不符合用户的预期。所以,Go语言不会自动为其生成该函数。因此,类型Integer只存在Less()方法,缺少Add()方法,不满足LessAdder接口,故上面的语句2不能赋值。

为了进一步证明以上的推理,不妨再定义一个Lesser接口:

  1. type Lesser interface {  
  2.     Less(b Integer) bool  

然后定义一个Integer类型的对象实例,将其赋值给Lesser接口:

  1. var a Integer = 1 
  2. var b1 Lesser = &a  // 语句1  
  3. var b2 Lesser = a   // 语句2  

正如我们所料,语句1和语句2均可以编译通过。我们再来讨论另一种情形:将一个接口赋值给另一个接口。在Go语言中,只要两个接口拥有相同的方法列表(次序不同不要紧),那么它们就是等同的,可以相互赋值。

下面来看一个示例,这是第一个接口:

  1. package one  
  2. type ReadWriter interface {  
  3.     Read(buf []byte) (n int, err error)  
  4.     Write(buf []byte) (n int, err error)  
  5. }  

第二个接口位于另一个包中:

  1. package two  
  2. type IStream interface {  
  3.     Write(buf []byte) (n int, err error)  
  4.     Read(buf []byte) (n int, err error)  
  5. }  

这里定义了两个接口,一个叫one.ReadWriter,另一个叫two.IStream,两者都定义了Read()、Write()方法,只是定义次序相反。one.ReadWriter先定义了Read()再定义了Write(),而two.IStream反之。

在Go语言中,这两个接口实际上并无区别,因为任何实现了one.ReadWriter接口的类,均实现了two.IStream;任何one.ReadWriter接口对象可赋值给two.IStream,反之亦然;在任何地方使用one.ReadWriter接口与使用two.IStream接口并无差异。

以下这些代码可编译通过:

  1. var file1 two.IStream = new(File)  
  2. var file2 one.ReadWriter = file1 
  3. var file3 two.IStream = file2 

接口赋值并不要求两个接口必须等价。如果接口A的方法列表是接口B的方法列表的子集,那么接口B可以赋值给接口A。例如,假设有Writer接口:

  1. type Writer interface {  
  2.     Write(buf []byte) (n int, err error)  
  3. }  

就可以将上面的one.ReadWriter和two.IStream接口的实例赋值给Writer接口:

  1. var file1 two.IStream = new(File)  
  2. var file4 Writer = file1 

但是反过来并不成立:

  1. var file1 Writer = new(File)  
  2. var file5 two.IStream = file1 // 编译不能通过  

这段代码无法编译通过,原因很显然:file1并没有Read()方法。


喜欢的朋友可以添加我们的微信账号:

51CTO读书频道二维码


51CTO读书会第9群:808517103

【责任编辑:book TEL:(010)68476606】

回书目   上一节   下一节
点赞 0
分享:
大家都在看
猜你喜欢

读 书 +更多

Wicked Cool Java中文版

本书主要介绍由Sun微系统公司创建的Java编程语言。 除了核心内容外,Java还有许多免费的财富,即开放源代码的库。本书就是为了介绍这些库...

订阅51CTO邮刊

点击这里查看样刊

订阅51CTO邮刊