摘录一段《Go语言编程》中的介绍:

Go语言的主要设计者之一 布 派 (Rob Pike) 经说过,如果只能选择一个Go语言的特 性  到其他语言中,他会选择接口。

接口在Go语言有着 关重要的地位。如果说goroutine和channel 是支 起Go语言的并发模型 的基 ,让Go语言在如 集群化与多核化的时代成为一道极为  的风景,那么接口是Go语言 整个类型系统的基 ,让Go语言在基础编程哲学的  上达到前所未有的高度。

Go语言在编程哲学上是变革派,而不是改良派。这不是因为Go语言有goroutine和channel, 而更重要的是因为Go语言的类型系统,更是因为Go语言的接口。Go语言的编程哲学因为有接口 而趋近完 。

Go面向对象的核心就是接口,interface。

1. 侵入式 vs 非侵入式

以往语言的接口一般都是侵入式的,就是实现类需要明确声明自己实现了某个接口。它的最大问题是接口的定义需要知道使用方也就是调用实现类的地方需要什么样的功能,不然实现类就不能很好的实现这个接口,可能某些需要的属性或者功能没有。但是这样就将接口的开发与其他功能的开发强耦合起来。而体现在工作上,就是接口开发的团队需要与业务团队经常沟通,任何需求上的变化都会触发接口的重新定义与实现。非常不友好。

Go是非侵入式的。在Go语言快速入门(一):基础篇3.2.1节中就有提到,我们可以为任意类型添加方法,对于struct类型也是这样。只要一个struct类型实现了某个interface定义的所有接口,我们就说这个struct类型实现了该接口。是不是非常easy。不需要明确指定我实现了那个接口,我实现的类可以说实现了很多接口。

这样当接口需求有变化时,只需要给某个struct类型添加相关的方法即可。

这样至少带来如下几个好处:

  • 不需要类库的继承树图了,java、python都有很庞大的类型继承关系图,Go就不需要,因为继承没有意义,只需要添加需要的方法即可
  • 实现类的时候,只需要考虑自己需要提供什么功能,不需要纠结接口拆的多细才合理。接口由使用方按需定义,不需要实现规划
  • 不用为了实现一个接口而导入一个包(或者库),减少了耦合,降低了风险

2. 接口赋值

接口赋值有两个方面,一是将一个对象赋值给接口,这样接口就可以调用这个对象的实现,二是将接口赋值给接口,这样可以用新接口名调用另一个接口所赋的值。

第一种,需要注意的时,如果接口中有定义修改调用者值的功能,如func (a *Integer) Add(b Integer),将一个对象赋给接口时需要用&符号,例如var b LessAdder = &a,而如果没有这种功能,用不用&符号都不关键,Go会正常处理相关逻辑。

第二种,接口赋值给接口,那么赋值符号左边的接口必须等于或者包含右边接口的所有功能,否则会失败。这很容易理解,接受了不符合自己定义的能力,会爆炸。

3. 接口查询

对于前面说的接口赋值,左边的能力要大于右边,如果某接口能力小于另外一个接口,可以执行什么操作呢?接口查询。

var file1 Writer = ...
if file5, ok := file1.(two.IStream); ok {
... }

这段代码就是查询一个弱能力的接口指向的对象,是否实现了一个强能力的接口定义的功能,如果是,则执行相关操作。

4. 类型查询

除了上面的接口查询,Go还支持直接查询某个对象是属于什么类型。类似与python中的type(),isinstance()等实现等功能。

var v1 interface{} = ...
switch v := v1.(type) {
  case int: ...
  case string: ...
}

5. 接口组合

任何东西都可以组合,包括接口。

type ReadWriter interface {
  Reader
  Writer
}

相当于:

type ReadWriter interface {
  Read(p []byte) (n int, err error)
  Write(p []byte) (n int, err error)
}

6. Any类型

由于任何对象实例都满足interface{}接口,所以interface{}接口类似于java.lang.Object在java中的位置。也就是可以将任何对象赋给interface{}接口。

func Println(args ...interface{})通过它实现了丰富的答应功能。

Go的面向对象就这些,没有更多了,但是我们在其他语言中需要的功能,通过这些简单机制就可以实现了,非常实用。

其他Go语言入门文章:

Go语言快速入门(一):基础篇

Go语言快速入门(三):并发编程