八、深入Go 编程语言接口
@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
- 点赞
- 收藏
- 关注作者
评论(0)