方法就是一个包含了接受者的函数(即指定了能够使用该方法的对象),接受者可以是命名类型或者结构体类型的一个值或一个指针。所有给定类型的方法属于该类型的方法集。
方法只是一个函数,它带有一个特殊的接收器类型,它是在func关键字和方法名之间编写的,接收器可以是struct类型或非struct类型,接收方可以在方法内部访问。
func (t Type)methodName(...)(...){
//
}
type Worker struct { name string age int } func (w Worker) work() { fmt.Println(w.name, " is working") }//方法可以用值或者指针调用,当调用者是指针时其实是(*w).name, //不过go语言中可以将*省略,于是就都是w.name func main() { w1 := Worker{"axuezm", 20} w1.work() fmt.Println(w1) w2 := new(Worker) w2.name = "xuezma" w2.age = 22 fmt.Println(w2) w2.work() }
tips:方法名可以相同,但同名方法的接受者不能一样
面向对象世界中的接口一般定义是“接口定义对象的行为”,它表示指定对象应该做什么,实现这种行为的方法是针对对象的。
在go语言中,接口是一组方法签名,当类型为(为:四声)接口中的所有方法提供定义时,它被称为实现接口。接口指定了类型应该具有的方法,类型决定了如何实现这些方法。
接口将所有具有共性的方法定义在一起,任何其他类型只要实现了这些方法就是实现了这个接口,如果某个对象实现了某个接口的所有方法,则此对象就实现了该接口。
设计接口的目的在于降低程序的耦合性,即各部分程序之间的关联性尽量降低,这样修改一部分代码时就不会牵扯到另外的代码。
go语言中,接口和类型的实现关系是非侵入式的。
type inferface_name interface{
method_name1()
method_name2()
...
}
//例子:定义一个USB接口,并创建鼠标以及U盘类实现了该接口 type USB interface { start() end() } type Mouse struct { name string } type UPan struct { name string } func (m Mouse) start() { fmt.Println(m.name, "start working") } func (m Mouse) end() { fmt.Println(m.name, "end working") } func (u UPan) start() { fmt.Println(u.name, "start working") } func (u UPan) end() { fmt.Println(u.name, "end working") } func printMes(usb USB) {//当需要接口类型的对象时,可以使用任意实现类对象代替 usb.start() usb.end() } func main() { m1 := Mouse{"shubiao1"} printMes(m1) u1 := UPan{"Upan1"} printMes(u1) //var usb USB //usb = m1 //usb.name()//接口对象不能访问实现类中的属性 }
空接口:不包含任何的方法,正因为如此,所有的类型都实现了空接口,因此空接口可以存储任意类型的数值
例如:
type A interface{ } type Cat struct{ name string } type Person{ name string } func main(){ var c1 A = Cat{"tonny"} var p1 A = Person{"axuezm"} var n1 A = "HELLOWORLD" var n2 A = 100 fmt.Println(c1) fmt.Println(p1) }
空接口的应用1:可以将某些函数的参数设置为空接口,即代表该函数可以接受任意类型的数据作为参数
func testfunc(a interface{}){//直接上匿名接口即可 fmt.Println(a) }
空接口的应用2:可以将某些容器存储的类型设置为空接口,这样该容器就能存储任意的类型了
m := make(map[string]interface{})//创建一个map,键是string类型,值为任意类型 m["name"] = "axuezm" m["age"] = 20 m["friend"] = Person{"xuezma",21}
一个类可以实现多个接口,但在声明的时候需要表示创建的是哪一个接口的实现
type A interface { test1() } type B interface { test2() } type C interface { A B test3() } type Cat struct { } func (cat Cat) test1() { fmt.Println("test1") } func (cat Cat) test2() { fmt.Println("test2") } func (cat Cat) test3() { fmt.Println("test3") } func main() { var cat Cat = Cat{} var a1 A = cat a1.test1() var b1 B = cat b1.test2() var c1 C = cat c1.test1() c1.test2() c1.test3() }
方式一:
1.instance := 接口对象.(实际类型) //不安全,断言错误会panic()恐慌
2.instance ,ok := 接口对象.(实际类型) //安全
方式二:switch
switch instance := 接口对象.(type){
case 实际类型1:
...
case 实际类型2:
...
...
}
接口断言是用来判断某个接口是否是某个具体的实现,如:
有一个接口,它有两个方法是求面积和周长,有一个三角形类和一个圆形类实现了这个接口。当你创建了一个接口对象并使用了三角形类的对象对它进行赋值时,它实际上还是一个接口类,不能调用三角形类中的属性和方法,此时使用接口断言可以判断该接口对象是不是三角形类。
package main import "fmt" type animal interface { shout() getColor() string } type Cat struct { color string foot int } type Dog struct { color string foot int } func (c Cat) shout() { fmt.Println("喵喵喵") } func (c Cat) getColor() string { return c.color } func (d Dog) shout() { fmt.Println("汪汪汪") } func (d Dog) getColor() string { return d.color } func getType(a animal) { /*************************方法一***************************/ if ins, ok := a.(Cat); ok { ins.shout() fmt.Printf("我是一只喵,我是%s,我有%d只脚\n", ins.getColor(), ins.foot) } else if ins, ok := a.(Dog); ok { ins.shout() fmt.Printf("我是一只汪,我是%s,我有%d只脚\n", ins.getColor(), ins.foot) } else { fmt.Println("俺也不知道俺是个啥") } } func getType2(a animal) { /***************************方法二**************************/ switch ins := a.(type) { case Cat: ins.shout() fmt.Printf("我是一只喵,我是%s,我有%d只脚\n", ins.getColor(), ins.foot) case Dog: ins.shout() fmt.Printf("我是一只汪,我是%s,我有%d只脚\n", ins.getColor(), ins.foot) default: fmt.Println("俺也不知道俺是个啥") } } func printMes(a animal) { a.shout() fmt.Println("我是", a.getColor()) } func main() { var cat Cat = Cat{"白色的", 4} printMes(cat) var dog Dog = Dog{"五彩斑斓的", 3} printMes(dog) var a animal = cat printMes(a) getType(cat) getType(dog) getType(a) var a1 animal getType(a1) getType2(a1) getType(cat) }
注:参数是接口类型的时候参数可以是接口类型的实现,可以是值传递也可以是指针传递,要注意值传递时会拷贝原数据,指针传递时会拷贝一个指针(指针指向的位置不会变,即一样会改变原数据)
go语言不是面向对象的语言,但我们可以通过结构体来模拟面向对象中的类以及继承
1.继承
模拟继承需要前文所讲的结构体嵌套以及匿名字段,在子类中增加一个字段为父类类型
type Person struct{ name string sex string } type Student struct{ Person school string score int }
2.提升字段
在结构体中属于匿名结构体的字段称为提升字段,因为它们可以被访问,就好像它们属于拥有匿名结构体字段的结构一样。
如在上面的例子中,Student结构体中的Person字段为匿名字段,同时它也是提升字段。
我们可以直接访问提升字段中的字段而不需要通过提升字段(有点拗口昂),如:
s1 := Student{} s1.name = "axuezm" s1.age = 18 s1.school = "nicai" s1.score = 100
以上例子中,本来name这个字段需要通过s1.Person.name来访问,但作为提升字段我们直接使用name就可以访问它了,而不需要通过提升字段Person
type student2 struct { p Person score int }//这样不是匿名字段的就不属于提升字段,不能直接通过student2的对象访问,必须要先访问p再访问p中的字段==》s2.p.name
3.子类可以直接调用父类的方法
func (p Person)eat(){ fmt.Println("父类:吃") } s1.eat()
子类可以重写父类的方法
func (p Student)eat(){ fmt.Println("子类:吃") }
4. 使用接口来模拟多态
对一个接口的实现来说:
看成实现本身的类型的话,它能够访问实现类中的属性和方法
看成是对应的接口类型的话,那就只能访问接口中的方法
一个函数如果接受接口类型作为参数,那么实际上可以传入该接口的任意实现类对象作为参数
定义一个类型为接口类型,实际上可以赋值为任意实现类的对象(但此时它不能访问实现类的属性和方法)