1 方法
//方法
1.是特殊的函数,可以自动传值 ---> 对象(go中就是结构体)来调用,自动把对象(当前结构体的实例)传过来
2.在func关键字 和 方法名 中间加入了一个特殊的接收器类型
接收器可以是结构体类型或者是非结构体类型
接收器是可以在方法的内部访问的
3.方法是绑定给结构体的 ---> 如何绑定
4.方法如何给非结构体类型绑定 ---> 将 非结构体类型 重命名,再绑定
eg:
type Myint int8
func (p *Myint) add(){
(*p) +=1
}
//总结:go中没有面向对象的概念,但是能够实现面向对象
1.结构体 ---> 类的一半,只有属性
2.结构体 + 匿名字段 ---> 类的继承
3.结构体 + 方法 ---> 类的另一半,类中的方法 实例化结构体后,可以自动传值(当前对象)
package main
import "fmt"
//定义了一个结构体
type Person1 struct {
name string
age int
}
//给结构体绑定方法
func (p Person1) PrintName() {
fmt.Println(p.name)
}
// (p Person1) 值类型接收器,接受的是当前结构体的实例(当前对象) 的copy一份
func (p Person1) ChangeName(name string) {
p.name = name //修改该结构体的实例 属性,不会影响原来结构体实例
fmt.Println(p)
}
// (p *Person1) 指针类型接收器,接受的是当前结构体的实例(当前对象) 的指针
就是python中的self,是原值的引用
func (p *Person1) ChangeAge(age int) {
p.age = age //修改该结构体的实例 属性,会影响原来结构体实例
fmt.Println(p)
}
//定义的是打印name的函数
func PrintName(p Person1) {
}
func main() {
//1 使用方法
var p Person1=Person1{"lqz",19}
p.PrintName()
//2 为什么我们已经有函数了还需要方法呢
区别在于:使用方式 和 自动传值
PrintName(p) //函数调用,需要手动传参
//3 指针接收器与值接收器 ---> 取别在于修改会不会影响原来的对象
var p Person1 = Person1{"lqz", 19}
p.ChangeName("彭于晏")
fmt.Println("修改后的:",p) //{"lqz", 19} 没有改
//5 接收器使用场景 ---> 如果想改原来的值,用指针类型接收器
var p Person1 = Person1{"lqz", 19}
p.ChangeAge(99)
fmt.Println("修改后的:",p) //{"lqz", 99} 修改
//5 匿名字段的方法 ---> 方法提升
p:=Person2{"lqz",18,Hobby2{"篮球",1}}
fmt.Println(p)
//p.hobby.printName()
p.printName() //方法提升了
}
//5 匿名字段的方法 ---> 方法提升
type Hobby2 struct {
hobbyName string
hobbyId int
}
type Person2 struct {
name string
age int
//hobby Hobby2
Hobby2 //匿名字段
}
func (h Hobby2) printName() {
fmt.Println(h.hobbyName)
}
2 接口
//接口: 就是python中的多态
1.一系列方法的集合,约束子类的行为
2.go也是鸭子类型,是非侵入式接口
//侵入式接口和非侵入式接口
非侵入式接口:删除接口后,基于该接口的结构体不会报错 eg: go
侵入式接口:删除接口后,基于该接口的类会报错 eg:java
package main
import (
"fmt"
)
//定义一个鸭子接口
type Duck interface {
run()
speak()
}
//定义一个空接口 ---> 所有类型都实现了空接口
type Empty interface {
}
//定义普通鸭结构体
type PDuck struct {
age int
name string
}
//定义唐老鸭结构体
type TDuck struct {
age int
name string
wife string
}
//让普通鸭实现Duck接口 ---> 只要实现了接口中所有方法,就叫实现该接口
func (p PDuck) run() {
fmt.Println("普通鸭子,走路歪歪扭扭")
}
func (p PDuck) speak() {
fmt.Println("普通鸭子,嘎嘎叫")
}
//让唐老鸭实现Duck接口 ---> 只要实现了接口中所有方法,就叫实现该接口
func (p TDuck) run() {
fmt.Println("人走路")
}
func (p TDuck) speak() {
fmt.Println("人叫")
}
func main() {
var p PDuck = PDuck{1, "肉鸭一号"}
var t TDuck = TDuck{5, "唐老鸭", "母唐老鸭"}
//唐老鸭和普通鸭,都可以赋值给鸭子接口类型,就可以都当做 鸭子接口 去操作
var d Duck
d = p
d = t
d.run()
d.speak()
//1 类型断言 d.(类型): 判断该接口是否 是这个类型,并返回该类型对应的值
作用:可以通过 接口类型,获取到原本类型的实例
返回值: 获取该对象、布尔值
if v,ok := d.(TDuck); ok{
fmt.Println(v.name)
}else {
fmt.Println("断言失败,不是唐老鸭类型")
}
//2 空接口类型 所有类型都可以赋值给空接口
var a Empty=1
a="lqz"
a=[]int{1,2,3}
//3 匿名空接口
interface{}
//5 类型选择 就是 类型断言 + Switch + 空接口 的结合
a := "lqz"
a := []int{1, 2, 3}
test(a)
t := TDuck{wife: "李易峰"}
test(t)
}
//5 类型选择 作用:可以通过 接口类型,获取到原本类型的实例
//func test(i interface{}){ //函数参数 是 匿名空接口
func test(i Empty) {
switch v:=i.(type) { // i.(type)可以返回i对应类型的值,但.(type)只能在switch内使用
case int:
fmt.Println("是int")
case string:
fmt.Println("是字符串")
case TDuck:
fmt.Println(v.wife)
fmt.Println("是唐老鸭类型")
default:
fmt.Println("不知道什么类型")
}
}
3 泛型
//泛型编程
通过引入 类型形参 和 类型实参 这两个概念
让一个函数获得了处理多种不同类型数据的能力
//理解案例:
定义一个int类型的add函数,但是不能处理int以外的类型
泛型就是将add函数的 参数类型不固定,等到实际传入参数时,再给定具体类型
从而实现同一个函数,处理 除类型以外的 相同逻辑的功能
python 没有泛型一说,但是可以通过 注释等提示,实现人为约束的 泛型
因为python本身就是动态类型,变量没有固定类型,可以指向所有类型
是一切皆对象,都是对象的引用
传入什么对象,就是什么对象的类型使用
//使用场景
如果你经常要分别为不同的类型写完全相同逻辑的代码,那么使用泛型将是最合适的选择
//具体实现 参考 https://segmentfault.com/a/1190000041634906
标签:Println,05,fmt,接口,---,泛型,func,类型,Go
From: https://www.cnblogs.com/Edmondhui/p/17287626.html