八、深入Go 编程语言接口

举报
毛利 发表于 2021/07/15 04:20:20 2021/07/15
【摘要】 @Author:Runsen 学习过Java,大家或多或少了解接口。接口是一种类型,它指定一个方法集,所有方法为接口类型就被认为是该接口。 文章目录 接口接口内部实现指针方法值方法空接口 接口 在Go语言中,一个接口类型总是代表着某一种类型(即所有实现它的类型)的行为。一个接口类型的声明通常会包含关键字type、类型名称、关键字interface以及由...

@Author:Runsen

学习过Java,大家或多或少了解接口。接口是一种类型,它指定一个方法集,所有方法为接口类型就被认为是该接口。

接口

在Go语言中,一个接口类型总是代表着某一种类型(即所有实现它的类型)的行为。一个接口类型的声明通常会包含关键字type、类型名称、关键字interface以及由花括号包裹的若干方法声明。go 使用 type 关键字来定义接口,接口的声明类似于结构体,使用类型别名且需要关键字 interface,语法如下。代码来源:菜鸟教程。

type Name interface { Method1(param_list) return_type Method2(param_list) return_type ...
}

  
 

go 中的接口,这个概念实际上是比较难懂的,来看一个demo:

package main

import "fmt"

type Person interface { getName() string
}

type student struct { Name string sex string
}

var stu = student{"Runsen","男"}

func (stu student) getName() string { return stu.Name
}  // 这样student类型就实现了Person接口

type teacher struct { Name string sex string
}

var tea = teacher{"Runsen","男"}
func (tea teacher) getName() string{ return tea.Name
}  // 这样teacher类型就实现了Person接口

func main() { fmt.Println(stu.getName()) //Runsen fmt.Println(tea.getName()) //Runsen
}

  
 

上面的代码定义了接口类型 Person ,接口中包含了一个不带参数、返回值为 string的方法 getName()。任何实现了方法 getName() 的类型 student和teacher,我们就说它实现了接口 Person 。

接口内部实现

接口内部的实现:

type iface struce{ tab *iTable data unsafe.Pointer
}

  
 

接口结构是包含两个字段的数据结构,第一个包含一个指向内部表的指针,这个内部表叫做iTable,包含子所存储的值的类型信息。iTable包含了已存储的值的类型信息已经值关联的一组方法。第二个字段指向存储值的指针,指向赋值的这个对象。

从内部实现来看,接口本身也是一种结构类型,只是编译器会对其做很多的限制。

  • 不能有自己的字段
  • 不能定义自己的方法
  • 只能声明方法,不能实现
  • 可嵌入其他接口类型

这些注意点和Java的接口完全一样。

指针方法值方法

实现接口有两种方法。上面我们都是使用值接受者(Value Receiver)来实现接口的。我们同样可以使用指针接受者(Pointer Receiver)来实现接口。

在此之前,我们需要了解下指针。
指针是存储变量内存地址的变量,表达了这个变量在内存存储的位置。就像我们常说:指针指向了变量。

Go 语言的取地址符是 &,放到一个变量前使用就会返回相应变量的内存地址。

package main

import "fmt"

func main() { var a int = 1 fmt.Printf("变量的地址: %x\n", &a) // 变量的地址: c0000140b8
}

  
 

在Go语言中关于指针的操作如下所示。

package main

import "fmt"

func main() { var a int= 1  /* 声明实际变量 */ var ip *int /* 声明指针变量 */ ip = &a  /* 指针变量的存储地址 */ fmt.Printf("a 变量的地址是: %x\n", &a  ) //a 变量的地址是: c0000140b8 /* 指针变量的存储地址 */ fmt.Printf("ip 变量储存的指针地址: %x\n", ip ) //ip 变量储存的指针地址: c0000140b8 /* 使用指针访问值 */ fmt.Printf("*ip 变量的值: %d\n", *ip ) //*ip 变量的值: 1
}

  
 

在函数接收者,可以使用指针方法和值方法。区别在于,用指针类型作为接收者,可以在函数/方法内部修改这个接收者的数据,而用值类型作为接收者,在函数/方法内部不能修改原接收者的数据。

接收者可以是指针类型,也可以是值类型,它们到底有什么区别?一开始学Go语言的时候,我也一头雾水。看下面的例子就明白了:

package main
import "fmt"

type user struct {
  name string
  age  int
}
// 值的
func (u user) PrintName() {
  fmt.Println(u.name)
}
// 指针的
func (u *user) PrintAge() {
  fmt.Println(u.age)
}
func main() { var Runsen *user // 润森是指针类型的
  Runsen = &user{"Runsen", 20}  // 正常输出无误
  Runsen.PrintName()  // zhangsan
  Runsen.PrintAge() // 10 var maoli user // 毛利是值类型
  maoli = user{"Maoli", 20} // 正常输出无误
  maoli.PrintName()  // lisi
  maoli.PrintAge() // 20

// 以值的方式调用 一个定义在指针类型下的方法时,会隐式的转换值到指针,例中maoli.PrintAge() 就是这样
// 以指针的方式调用 一个定义在值类型下的方法时,会隐式的转换指针到值,例中Runsen.PrintName() 就是这样
// 相同:无论定义在哪种类型下,都可以访问到。
// 区别:只有定义在指针类型下的方法可以修改原变量的内容
}

  
 

指针类型的接收者之所以需要指针,就是因为它要改变原对象的值。 在上面的例子中,调用user的SetName方法时,编译器会帮你把user的指针传递给SetName方法。

空接口

一个不包含任何方法的接口,称之为空接口,形如:interface{}。因为空接口不包含任何方法,所以任何类型都默认实现了空接口。

举个例子,fmt 包中的 Println() 函数,可以接收多种类型的值,比如:int、string、array等。为什么,因为它的形参就是接口类型,可以接收任意类型的值。

下面就是Println接口源码

func Println(a ...interface{}) (n int, err error) {}

  
 

空接口表示为 interface{}。由于空接口没有方法,因此所有类型都实现了空接口。

package main

import  "fmt"

func describe(i interface{}) { fmt.Printf("Type = %T, value = %v\n", i, i)
}

func main() { s := "Hello World" describe(s) //Type = string, value = Hello World i := 1 describe(i) // Type = int, value = 1 strt := struct { name string }{ name: "Runsen", } describe(strt) //Type = struct { name string }, value = {Runsen}

}

  
 

感谢各位的阅读,文章的目的是分享对知识的理解,技术类文章我都会反复求证以求最大程度保证准确性,若文中出现明显纰漏也欢迎指出,我们一起在探讨中学习。

文章来源: maoli.blog.csdn.net,作者:刘润森!,版权归原作者所有,如需转载,请联系作者。

原文链接:maoli.blog.csdn.net/article/details/108022136

【版权声明】本文为华为云社区用户转载文章,如果您发现本社区中有涉嫌抄袭的内容,欢迎发送邮件进行举报,并提供相关证据,一经查实,本社区将立刻删除涉嫌侵权内容,举报邮箱: cloudbbs@huaweicloud.com
  • 点赞
  • 收藏
  • 关注作者

评论(0

0/1000
抱歉,系统识别当前为高风险访问,暂不支持该操作

全部回复

上滑加载中

设置昵称

在此一键设置昵称,即可参与社区互动!

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。