// 利用函数类型变量保存一个函数 fn = say // 利用函数类型变量调用一个函数 fn() }
方法只能通过绑定类型的变量调用, 函数可以直接调用
package main import 'fmt' type Person struct { name string age int } // 定义一个方法 func (p Person)say() { fmt.Println('my name is', p.name, 'my age is', p.age) } // 定义一个函数 func test() { fmt.Println('test') } func main() { per := Person{'lnj', 33} per.say() // 方法只能通过绑定类型的变量调用 //say() // 编译报错, 不能直接调用 test() // 编译通过, 可以直接调用 }
方法的接收者可以看做就是函数的一个形参
package main import 'fmt' type Person struct { name string age int } // 定义一个方法 func (p Person)say() { fmt.Println('my name is', p.name, 'my age is', p.age) } // 定义一个函数 func test(p Person) { fmt.Println('my name is', p.name, 'my age is', p.age) } func main() { per := Person{'lnj', 33} per.say() // my name is lnj my age is 33 test(per) // my name is lnj my age is 33 }
既然可以看做形参, 那么自然也具备形参的特点(值传递和地址传递)
package main import 'fmt' type Person struct { name string age int } // 接收者是一个变量 func (p Person)setName(name string) { p.name = name } // 接收者是一个指针 func (p *Person)setAge(age int) { p.age = age } func main() { per := Person{'lnj', 33} fmt.Println(per) // {lnj 33} // 值传递, 方法内部修改不会影响方法外部 per.setName('zs') fmt.Println(per) // {lnj 33} p := &per // 地址传递, 方法内部修改会影响方法外部 (*p).setAge(18) fmt.Println(per) // {lnj 18} }
地址传递的几种调用方式
package main import 'fmt' type Person struct { name string age int } // 接收者是一个变量 func (p Person)setName(name string) { p.name = name } // 接收者是一个指针 func (p *Person)setAge(age int) { p.age = age } func main() { per := Person{'lnj', 33} // 方式一: 先拿到指针,然后再通过指针调用 p := &per (*p).setAge(18) fmt.Println(per) // {lnj 18} // 方式二: 直接利用变量调用, 底层会自动获取变量地址传递给接收者 per.setAge(66) fmt.Println(per) // {lnj 66} }
package main import 'fmt' type aer interface { fna() } type ber interface { aer fnb() } // Person实现了超集接口所有方法 type Person struct {} func (p Person)fna() { fmt.Println('实现A接口中的方法') } func (p Person)fnb() { fmt.Println('实现B接口中的方法') } // Student实现了子集接口所有方法 type Student struct { } func (p Student)fna() { fmt.Println('实现A接口中的方法') } func main() { var i ber // 子集接口变量不能转换为超集接口变量 //i = Student{} fmt.Println(i) var j aer // 超集接口变量可以自动转换成子集接口变量, j = Person{} fmt.Println(j) }
空接口类型可以接收任意类型数据
package main import 'fmt' func main() { // 1.定义一个空接口类型变量 var i interface{} // 2.用接口类型保存任意类型数据 i = 123 fmt.Println(i) // 123 i = 3.14 fmt.Println(i) // 3.14 i = 'lnj' fmt.Println(i) // lnj i = [3]int{1, 3, 5} fmt.Println(i) // [1 3 5] i = []int{2, 4, 6} fmt.Println(i) // [2 4 6] i = map[string]string{'name': 'lnj'} fmt.Println(i) // map[name:lnj] i = Computer{'戴尔', 'F1234'} fmt.Println(i) // {戴尔 F1234} }
只要是自定义类型就可以实现接口
package main import 'fmt' // 1.定义一个接口 type usber interface { start() stop() } // 2.自定义int类型 typeinteger int // 2.实现接口中的所有方法 func (i integer)start() { fmt.Println('int类型实现接口') } func (i integer)stop() { fmt.Println('int类型实现接口') } func main() { var i integer = 666 i.start() // int类型实现接口 i.stop() // int类型实现接口 }
接口类型转换
接口类型变量可以接收实现了该接口类型的变量, 但是只能调用该变量中的方法, 不能访问该变量的属性
package main import 'fmt' type studier interface { read() } type Person struct { name string age int } func (p Person)read() { fmt.Println(p.name, '正在学习') } func main() { // 1.定义一个接口类型变量 var s studier // 2.用接口类型变量接收实现了接口的结构体 s = Person{'lnj', 33} s.name = 'zs' // 报错, 由于s是接口类型, 所以不能访问属性 fmt.Println(s) }
想要访问变量中的属性, 必须将接口类型还原为原始类型
package main import 'fmt' type studier interface { read() } type Person struct { name string age int } func (p Person)read() { fmt.Println(p.name, '正在学习') } func main() { var s studier s = Person{'lnj', 33} s.name = 'zs' // 报错, 由于s是接口类型, 所以不能访问属性 // 2.定义一个结构体类型变量 //var p Person // 不能用强制类型转换方式将接口类型转换为原始类型 //p = Person(s) // 报错
// 2.利用ok-idiom模式将接口类型还原为原始类型 // s.(Person)这种格式我们称之为: 类型断言 if p, ok := s.(Person); ok { p.name = 'zs' fmt.Println(p) }
// 2.通过 type switch将接口类型还原为原始类型 // 注意: type switch不支持fallthrought switch p := s.(type) { case Person: p.name = 'zs' fmt.Println(p) // {zs 33} default: fmt.Println('不是Person类型') } }
package main import 'fmt' type studier interface { read() } type Person struct { name string age int } func (p Person)read() { fmt.Println(p.name, '正在学习') } func main() { // 1.定义一个抽象接口类型 var i interface{} i = Person{'lnj', 33} // 不能调用read方法, 因为抽象接口中没有这个方法 //i.read() // 2.利用ok-idiom模式将抽象接口转换为具体接口 if s, ok := i.(studier); ok{ // 可以调用read方法,因为studier中声明了这个方法,并且结构体中实现了这个方法 s.read() // lnj 正在学习 } }