这是我参与8月更文挑战的第13天,活动详情查看:8月更文挑战
在Go中,接口是一组方法签名。当类型为接口中的所有方法提供定义时,它被称为实现接口。它与OOP非常相似。接口指定了类型应该具有的方法,类型决定了如何实现这些方法。
它把所有的具有共性的方法定义在一起,任何其他类型只要实现了这些方法就是实现了这个接口
接口定义了一组方法,如果某个对象实现了某个接口的所有方法,则此对象就实现了该接口。
接口的定义语法
/* 定义接口 */
type interface_name interface {
method_name1 [return_type]
method_name2 [return_type]
}
/* 定义结构体 */
type struct_name struct {
/* variables */
}
/* 实现接口方法 */
func (struct_name_variable struct_name) method_name1() [return_type] {
/* 方法实现 */
}
...
func (struct_name_variable struct_name) method_namen2() [return_type] {
/* 方法实现*/
}
示例代码:
package main
import (
"fmt"
)
type Phone interface {
call()
}
type NokiaPhone struct {
}
func (nokiaPhone NokiaPhone) call() {
fmt.Println("I am Nokia, I can call you!")
}
type IPhone struct {
}
func (iPhone IPhone) call() {
fmt.Println("I am iPhone, I can call you!")
}
func main() {
var phone Phone
phone = new(NokiaPhone)
phone.call()
phone = new(IPhone)
phone.call()
}
运行结果:
I am Nokia, I can call you!
I am iPhone, I can call you!
示例代码:
package main
import (
"fmt"
)
type USB interface {
Name() string
PlugIn()
}
type FlashDisk struct {
name string
}
func (fd FlashDisk)Name() string {
return fd.name
}
func (fd FlashDisk) PlugIn() {
fmt.Println(fd.name,"连入电脑中。。")
}
type Mouse struct {
name string
}
func (m Mouse)Name() string {
return m.name
}
func (m Mouse) PlugIn() {
fmt.Println(m.name,"连入电脑,准备工作。。")
}
func main() {
/*
接口:是一个方法或多个方法声明的集合
只要某个类型拥有该接口的所有方法的声明,就算实现该接口。无需显示声明实现了哪个接口,
这称为structural typing
接口声明只有方法声明,没有方法实现,没有数据字段
*/
fd := FlashDisk{"U盘"}
fmt.Println(fd.Name())
fd.PlugIn()
m1:=Mouse{"鼠标"}
fmt.Println(m1.Name())
m1.PlugIn()
}
运行结果:
U盘
U盘 连入电脑中。。
鼠标
鼠标 连入电脑,准备工作。。
- interface可以被任意的对象实现
- 一个对象可以实现任意多个interface
- 任意的类型都实现了空interface(我们这样定义:interface{}),也就是包含0个method的interface
interface值
示例代码:
package main
import "fmt"
type Human struct {
name string
age int
phone string
}
type Student struct {
Human //匿名字段
school string
loan float32
}
type Employee struct {
Human //匿名字段
company string
money float32
} //Human实现Sayhi方法
func (h Human) SayHi() {
fmt.Printf("Hi, I am %s you can call me on %s\n", h.name, h.phone)
} //Human实现Sing方法
func (h Human) Sing(lyrics string) {
fmt.Println("La la la la...", lyrics)
} //Employee重写Human的SayHi方法
func (e Employee) SayHi() {
fmt.Printf("Hi, I am %s, I work at %s. Call me on %s\n", e.name,
e.company, e.phone) //Yes you can split into 2 lines here.
}
// Interface Men被Human,Student和Employee实现
// 因为这三个类型都实现了这两个方法
type Men interface {
SayHi()
Sing(lyrics string)
}
func main() {
mike := Student{Human{"Mike", 25, "222-222-XXX"}, "MIT", 0.00}
paul := Student{Human{"Paul", 26, "111-222-XXX"}, "Harvard", 100}
sam := Employee{Human{"Sam", 36, "444-222-XXX"}, "Golang Inc.", 1000}
Tom := Employee{Human{"Sam", 36, "444-222-XXX"}, "Things Ltd.", 5000}
//定义Men类型的变量i
var i Men
//i能存储Student
i = mike
fmt.Println("This is Mike, a Student:")
i.SayHi()
i.Sing("November rain")
//i也能存储Employee
i = Tom
fmt.Println("This is Tom, an Employee:")
i.SayHi()
i.Sing("Born to be wild")
//定义了slice Men
fmt.Println("Let's use a slice of Men and see what happens")
x := make([]Men, 3)
//T这三个都是不同类型的元素,但是他们实现了interface同一个接口
x[0], x[1], x[2] = paul, sam, mike
for _, value := range x {
value.SayHi()
}
}
那么interface里面到底能存什么值呢?如果我们定义了一个interface的变量,那么这个变量里面可以存实现这个interface的任意类型的对象。例如上面例子中,我们定义了一个Men interface类型的变量m,那么m里面可以存Human、Student或者Employee值
当然,使用指针的方式,也是可以的
但是,接口对象不能调用实现对象的属性
示例代码:
package main
import "fmt"
type Run interface{
start()
end()
}
type Runner struct {
name string
}
type Human2 struct {
Runner
age int
}
type Pig struct{
Runner
Color string
}
func (r Runner) start() {
fmt.Println(r.name,",开始跑。。")
}
func (r Runner) end() {
fmt.Println(r.name,"停止跑步。。")
}
// 方法重写
func (h Human2) start() {
fmt.Println(h.name, "今年",h.age,"岁了,跑步前需要先热身。。然后开始跑步。。")
}
func main() {
h1:=Human2{Runner{"张三"},30}
p1:=Pig{Runner{"小猪佩奇"},"粉色"}
h1.start()
h1.end()
p1.start()
p1.end()
fmt.Println("------------------")
// 定义接口类型变量,可以指向实现类的对象。模拟多态
var r1 Run
r1 = h1
r1.start()
r1.end()
fmt.Println(h1.age)
//fmt.Println(r1.age) // 接口类型对象,不能访问具体的子类对象的字段
fmt.Printf("%T,%T\n",h1,r1) // main.Human2,main.Human2
var r2 Run
r2 = p1
r2.start()
r2.end()
fmt.Println("-------------")
// 创建一个数组:定义为接口类型,但实际上可以存入任意的子类对象
arr := [2]Run{h1,p1}
for _,v := range arr{
v.start()
v.end()
}
testRun(p1)
testRun(r1)
}
func testRun(r Run) { // 定义一个函数,接收一个借口类型的对象作为参数。
fmt.Println("测试赛跑者。。。")
r.start()
r.end()
}
运行结果:
张三 今年 30 岁了,跑步前需要先热身。。然后开始跑步。。
张三 停止跑步。。
小猪佩奇 ,开始跑。。
小猪佩奇 停止跑步。。
------------------
张三 今年 30 岁了,跑步前需要先热身。。然后开始跑步。。
张三 停止跑步。。
30
main.Human2,main.Human2
小猪佩奇 ,开始跑。。
小猪佩奇 停止跑步。。
-------------
张三 今年 30 岁了,跑步前需要先热身。。然后开始跑步。。
张三 停止跑步。。
小猪佩奇 ,开始跑。。
小猪佩奇 停止跑步。。
测试赛跑者。。。
小猪佩奇 ,开始跑。。
小猪佩奇 停止跑步。。
测试赛跑者。。。
张三 今年 30 岁了,跑步前需要先热身。。然后开始跑步。。
张三 停止跑步。。
interface函数参数
interface的变量可以持有任意实现该interface类型的对象,这给我们编写函数(包括method)提供了一些额外的思考,我们是不是可以通过定义interface参数,让函数接受各种类型的参数。
package main
import (
"fmt"
)
type sharp interface {
area() float64
}
type sqrt struct{
l float64
}
func (s sqrt) area() float64 {
return s.l * s.l
}
type circle struct{
r float64
}
func (c circle)area() float64 {
return c.r * 3.14 * c.r
}
// 接口类型作为参数
func getArea(s sharp) {
fmt.Println(s.area())
}
func main() {
/*
接口作为参数
*/
s1:=sqrt{6.5}
c1:=circle{2.5}
getArea(s1)
getArea(c1)
}
运行结果:
42.25
19.625