本文共 3609 字,大约阅读时间需要 12 分钟。
@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 mainimport "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 mainimport "fmt"func main() { var a int = 1 fmt.Printf("变量的地址: %x\n", &a) // 变量的地址: c0000140b8}
在Go语言中关于指针的操作如下所示。
package mainimport "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 mainimport "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 mainimport "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}}
感谢各位的阅读,文章的目的是分享对知识的理解,技术类文章我都会反复求证以求最大程度保证准确性,若文中出现明显纰漏也欢迎指出,我们一起在探讨中学习。
转载地址:https://maoli.blog.csdn.net/article/details/108022136 如侵犯您的版权,请留言回复原文章的地址,我们会给您删除此文章,给您带来不便请您谅解!