前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >Go 语言面向对象教程 —— 接口篇:接口赋值

Go 语言面向对象教程 —— 接口篇:接口赋值

作者头像
学院君
发布2019-08-08 10:04:41
1.4K0
发布2019-08-08 10:04:41
举报
文章被收录于专栏:学院君的专栏

上篇教程我们介绍了接口定义及实现,和 PHP 一样,Go 语言的接口不支持直接实例化,只能通过实现类实现接口声明的所有方法,不过不同之处在于 Go 语言接口支持赋值操作,从而快速实现接口与实现类的映射,与之相比,PHP 要实现接口与实现类的映射,只能基于 IoC 容器通过依赖注入实现,就像 Laravel 框架底层服务容器所做的那样,要复杂的多。

接口赋值在 Go 语言中分为如下两种情况:

  • 将实现接口的对象实例赋值给接口;
  • 将一个接口赋值给另一个接口。

下面我们通过代码实例逐个介绍对应的实现和注意事项。

将对象实例赋值给接口

先看看将指定类型的对象实例赋值给接口,这要求该对象对应的类实现了接口要求的所有方法,这个是自然,否则也就不能算作实现该接口了,例如之前我们在为基本类型添加方法这篇教程中定义过一个 Integer 类型:

代码语言:javascript
复制
type Integer int

func (a Integer) Equal(i Integer) bool {
    return a == i
}

func (a Integer) LessThan(i Integer) bool {
    return a < i
}

func (a Integer) MoreThan(i Integer) bool {
    return a > i
}

func (a *Integer) Increase(i Integer) {
    *a = *a + i
}

func (a *Integer) Decrease(i Integer) {
    *a = *a - i
}

相应地,我们定义一个接口 IntNumber

代码语言:javascript
复制
type IntNumber interface {
    Equal(i Integer) bool
    LessThan(i Integer) bool
    MoreThan(i Integer) bool
    Increase(i Integer)
    Decrease(i Integer)
}

安装 Go 语言的约定,Integer 类型实现了 IntNumber 接口。然后我们可以这样将 Integer 类型对应的对象实例赋值给 IntNumber 接口:

代码语言:javascript
复制
var a Integer = 1
var b IntNumber = &a

注意到上述赋值语句中,我们将对象实例 a 的指针应用赋值给了接口变量,为什么要这么做呢?因为 Go 语言会根据类似下面这样的非指针成员方法:

代码语言:javascript
复制
func (a Integer) Equal(i Integer) bool

自动生成一个新的与之对应的指针成员方法:

代码语言:javascript
复制
func (a *Integer) Equal(i Integer) bool {
    return (*a).Equal(i)
}

这样一来,类型 *Integer 就存在所有 IntNumber 接口中声明的方法,而 Integer 类型不包含指针方法 IncreaseDecrease(关于这一点我们前面已经介绍过),所以严格来说,只有 *Integer 类型实现了 IntNumber 接口。如果我们贸然将 a 的值引用赋值给 b,编译时会报错:

代码语言:javascript
复制
cannot use a (type Integer) as type IntNumber in assignment:
    Integer does not implement IntNumber (Decrease method has pointer receiver)

显然,如果 Integer 类中实现的方法不是指针方法,则进行接口赋值时,传递对象实例的值引用给接口变量即可,否则需要传递指针变量。为了验证这一点,我们可以再创建一个新的接口 IntNumber2

代码语言:javascript
复制
type IntNumber2 interface {
    Equal(i Integer) bool
    LessThan(i Integer) bool
    MoreThan(i Integer) bool
}

然后将对象实例 a 的值引用赋值给 IntNumber 接口变量:

代码语言:javascript
复制
var a Integer = 1
var b1 IntNumber = &a
var b2 IntNumber2 = a

则上面两条接口赋值语句都可以编译通过。

将接口赋值给接口

接下来我们来看将一个接口赋值给另一个接口:在 Go 语言中,只要两个接口拥有相同的方法列表(与顺序无关),那么它们就是等同的,可以相互赋值。

下面我们来编写对应的示例代码,这是第一个接口 Number1,位于 oop1 包中:

代码语言:javascript
复制
package oop1

type Number1 interface {
    Equal(i int) bool
    LessThan(i int) bool
    MoreThan(i int) bool
}

这是第二个接口 Number2,位于 oop2 包中:

代码语言:javascript
复制
package oop2

type Number2 interface {
    Equal(i int) bool
    MoreThan(i int) bool
    LessThan(i int) bool
}

这里我们定义了两个接口,一个叫 oop1.Number1,一个叫 oop2.Number2,两者都定义五个相同的方法,只是顺序不同而已。在 Go 语言中,这两个接口实际上并无区别,因为:

  • 任何实现了 oop1.Number1 接口的类,也实现了 oop2.Number2
  • 任何实现了 oop1.Number1 接口的对象实例都可以赋值给 oop2.Number2,反之亦然;
  • 在任何地方使用 oop1.Number1 接口与使用 oop2.Number2 并无差异。

接下来我们定义一个实现了这两个接口的类 Number

代码语言:javascript
复制
type Number int;

func (n Number) Equal(i int) bool {
    return int(n) == i;
}

func (n Number) LessThan(i int) bool {
    return int(n) < i;
}

func (n Number) MoreThan(i int) bool {
    return int(n) > i;
}

所以下面这些赋值代码都是合法的,会编译通过:

代码语言:javascript
复制
var num1 Number = 1;
var num2 oop1.Number1 = num1
var num3 oop2.Number2 = num2

此外,接口赋值并不要求两个接口完全等价(方法完全相同)。如果接口 A 的方法列表是接口 B 的方法列表的子集,那么接口 B 可以赋值给接口 A。例如,假设 Number2 接口定义如下:

代码语言:javascript
复制
type Number2 interface {
    Equal(i int) bool
    MoreThan(i int) bool
    LessThan(i int) bool
    Add(i int)
}

要让 Number 类继续保持实现这两个接口,需要在 Number 类定义中新增一个 Add 方法实现:

代码语言:javascript
复制
func (n *Number) Add(i int) {
    *n = *n + Number(i);
}

接下来,将上面的接口赋值语句改写如下即可:

代码语言:javascript
复制
var num1 oop1.Number = 1;
var num2 oop2.Number2 = &num1;
var num3 oop1.Number1 = num2;

但是反过来不行:

代码语言:javascript
复制
var num1 oop1.Number = 1;
var num2 oop1.Number1 = &num1;
var num3 oop2.Number2 = num2;   // 这一段编译出错

因为 Number1 接口中没有 Add 方法,这一点和 PHP/Java 中子类实例可以直接赋值给父类,而父类实例不能直接赋值给子类颇有些异曲同工。

本文参与 腾讯云自媒体同步曝光计划,分享自微信公众号。
原始发表:2019-07-30,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 极客书房 微信公众号,前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 将对象实例赋值给接口
  • 将接口赋值给接口
相关产品与服务
容器服务
腾讯云容器服务(Tencent Kubernetes Engine, TKE)基于原生 kubernetes 提供以容器为核心的、高度可扩展的高性能容器管理服务,覆盖 Serverless、边缘计算、分布式云等多种业务部署场景,业内首创单个集群兼容多种计算节点的容器资源管理模式。同时产品作为云原生 Finops 领先布道者,主导开源项目Crane,全面助力客户实现资源优化、成本控制。
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档