写在前面:

虽然是一名入坑不久golang小白,跌跌撞撞的也在golang的海洋里摸索了一年,使用golang在生产环境完成了一些项目,时至今日,发现自己有的知识并不能很好的融会贯通,究其缘由,发现还是由于自己没有做笔记的习惯,平台零零散散的记录随着时间流逝不知所踪,因此,借助知乎这个平台,希望自己在工作之余,能够落地一些学习笔记,无论是搬运也好,原创也罢,希望自己能够在学习中不断积累,助推自己进步。


在Go语言中,一般有以下几种内置的基础数据类型:

  • 布尔型:bool
  • 整型:int、byte、uint 等
  • 浮点型:float32、float64
  • 字符串:string
  • 错误:error
  • rune

除了以上几种具体的内置数据类型外,同java,c++等语言一样,golang也具备自己的抽象数据类型——接口,它不会暴露它所代表的对象的内部值的结构,而是展示出它们自己方法。简单来说,接口是一组方法的集合,当一个类型为接口中的所有方法提供定义时,它被称为实现该接口。

就目前而言,市场上大多数编程语言的接口都是侵入式的,也就是使用接口时要说明,我实现了某某接口,比如java,想要实现一个接口,就需要使用implements关键字然后加上接口名字。而Go 语言的接口引入了“非侵入式”概念,我们不需要显式的说明实现了哪个接口,只需要根据我们已有的方法来判断就可以。

golang中除了常规接口外,还提供了空接口,空接口不包含任何方法,对实现不做任何要求,类似 Java/C# 中所有类的基类:

type Any interface{}

空接口里面没有方法,所以它也不具有任何能力,其作用相当于 Java 的 Object 类型,可以容纳任意对象,它是一个万能容器,如果一个接口里面没有定义任何方法,那么它就是空接口,任意结构体都隐式地实现了空接口。

最常用到的 fmt 标准库中的 Print() 系列方法,可以接受任意类型的参数:

func Printf(format string, a ...interface{}) (n int, err error) {
    return Fprintf(os.Stdout, format, a...)
}

func Fprintln(w io.Writer, a ...interface{}) (n int, err error) {
    p := newPrinter()
    p.doPrintln(a)
    n, err = w.Write(p.buf)
    p.free()
    return
}

接口的一般使用基于自己编写的demo进行,本文主要内容也是基于此项目进行,项目的结构如下:

├─.idea
├─animal  
├─base
└─people

首先我们定义接口,在例子中,我们定定义了3个接口,并且golang中,接口支持组合继承,因此我们可以将两个接口定义合并为一个接口如下,比如demo中的people和animal继承了action接口。

type Action interface {
	Smellable()   // smell
	Eatable()  // eat
}

type People interface {
	Action()   // action
	Walk()  // Walk
}

type Animal interface {
	Action()   // Action
	Crawl()  // Crawl
}

然后我们写一个实体去实现这个接口

package people

import (
	"fmt"
)

type Chinese struct {
	Name   string
	High    int
	Weight  int
	Country string
}

var instance *Chinese

func NewChinese() *Chinese {
	if instance == nil {
		instance = &Chinese{
			Country: "中国",
		}
	}
	return instance
}

func (c Chinese) Walk()  {
	fmt.Println("散步....")
}

func (c Chinese) Smellable() {
	fmt.Println("可以闻")
}

func (c Chinese) Eatable() {
	fmt.Println("可以吃")
}

func (c Chinese) Action()  {
	c.Eatable()
	c.Smellable()
}

可以看出,Chinese这个结构体具备了接口中people对应的所有方法,因此Chinese隐式的实现了people接口,并且也实现了Action接口。

当我们在main方法中运行实例,即可以打印效果

package main

import (
	"fmt"
	"golangDemo/animal"
	"golangDemo/base"
	"golangDemo/people"
)

func main()  {
	c := people.NewChinese()
	c.Name = "小明"
	// 以下代码段可以实现属性覆盖
	/*c = &people.Chinese{
		Name:    "张三",
		High:    160,
		Weight:  90,
		Country: "中国",
	}*/
	//base.DoAction(c)
	fmt.Println(c.Name,"来自",c.Country)
	base.IsPeople(c)
}

执行效果如下

可以吃
可以闻

小明 来自 中国
散步....
可以吃
可以闻

可以看出,Chinese这个实体实现了people这个接口,同时实现了Action这个接口,因此可以打印对应实现的信息。


有了简单的接口实现例子,我们就可以基于业务需求,在对象实体中设置对应的属性,来进行自定义的接口实现,首先,我们定义Chinese和Animal两个实体对象:

type Chinese struct {
	Name   string
	High    int
	Weight  int
	Country string
}

var instance *Chinese

func NewChinese() *Chinese {
	if instance == nil {
		instance = &Chinese{
			Country: "中国",
		}
	}
	return instance
}

type Animal struct {
	Name   string	
	High   int	
	Weight int	
	Kinds  string	
	Word   string	
}
var instance *Animal

func NewAnimal() *Animal {
	if instance == nil {
		instance = &Animal{
			Word: "我是人类的好朋友!",
		}
	}
	return instance
}

接下来,我们根据对应的属性,设置对应的操作行为,即getter和setter方法,以animal为例:

func (a *Animal) SetName(name string)  {
	a.Name = name
}

func (a *Animal) SetHigh(high int)  {
	a.High = high
}

func (a *Animal) SetWeight(weight int)  {
	a.Weight = weight
}

func (a *Animal) SetKinds(kinds string)  {
	a.Kinds = kinds
}

func (a Animal) GetName() string {
	return a.Name
}

func (a Animal) GetHigh() int {
	return a.High
}

func (a Animal) GetWeight() int  {
	return a.Weight
}

func (a Animal) GetKinds() string  {
	return a.Kinds
}

此处需要注意的是,对于结构体属性的操作,在set方法中需要使用func (a *Animal),而get方法则不需要,原因详见链接:How to set and get fields in struct's method

有了接口实现,以及实体对象的属性操作,我们在具体场景中,就可以根据具体对象的不同个体,返回具有同样特性【接口】的结果,达到同类型不同属性对象的操作,示例如下:

func main()  {
	a := animal.NewAnimal()
	a.SetName("旺财")
	a.SetKinds("宠物")

	fmt.Println(a.GetName(),"是",a.GetKinds(),a.Name,"说",a.Word)
	base.DoAction(a)
	a.Crawl()
}

对应接口实现:

package animal

import "fmt"

var instance *Animal

func NewAnimal() *Animal {
	if instance == nil {
		instance = &Animal{
			Word: "我是人类的好朋友!",
		}
	}
	return instance
}

func (a Animal) SetWord(key string)  {
	a.Word = key
}

func (a Animal) Crawl()  {
	fmt.Println("蹦蹦跳跳....")
}

func (a Animal) Smellable() {
	fmt.Println("可以闻")
}

func (a Animal) Eatable() {
	fmt.Println("可以吃")
}

func (a Animal) Action()  {
	a.Smellable()
	a.Eatable()
}

执行结果如下,符合预期:

旺财 是 宠物 旺财 说 我是人类的好朋友!
可以吃
可以闻
蹦蹦跳跳....

在本文中,我们简单了解了接口的定义和实现,以及了解了golang中接口的组合继承,并结合具体实例操作简单示范了同一类实体对应的操作,由于是第一次写笔记,可能存在很多漏洞和不足,欢迎评论留言,大家共同进步。