数据类型:
- 简单类型:布尔 整型 浮点型 复数(字符) 字符串
- 复杂类型:数组 切片 字典 结构 指针 函数类型
类型系统分为:
- 命名系统
- 非命名系统
- 底层类型
- 动态类型
- 静态类型
自定义类型,类型方法
类型简介
命名类型 未命名类型
命名类型:
类型可以通过标识符表示。包括:
- 20个预声明简单类型
如:int int8 int16 int32 int64
uint uint8 uint16 uint32 uint64 uintptr
float32 float64 complex128 complex64
bool byte rune string error
https://blog.youkuaiyun.com/yulijia/article/details/81738479 - 自定义类型:
type new old
type A int
(int是预声明类型,),
type Person struct{a int}
,type B []int
([]int类型字面量/未命名类型)
type B A
(A是自定义类型)
这三种。上面的A,Person, B 都是自定义类型
未命名类型
预声明类型, 关键字, 操作符 组合而成。又叫 类型字面量,又叫复合类型
包括:
数组( array )、切片( s li ce )、字典( map )、通道( channel )、指针( pointer ) 、函数字面量( functi on )、结构( struct )和接口( 没有用type定义的接口和结构〕都属于类型字面量,也都是未命名类型。
如:
a := struct{
name string
age int
}{"Ana", 18}
var b []int//切片
type Person struct{
name string
age int
}
aa:=Person{"Ana", 18},//这里因为Person是命名类型,所以aa是命名类型的变量
底层类型
用于类型赋值 类型强转。
预声明类型和类型字面量的底层类型是自身
自定义类型的底层类型逐渐向下查找,直到找到预声明类型或者类型字面量
type Tl string //string
type T2 Tl //string
type T3 []string //[]string
type T4 T3//[]string
type T5 []Tl//[]T1 注意这里[]T1是一个整体,T5的底层类型是[]T1,不是[]string, 尽管T1的底层类型是string
type T6 T5//[]T1
类型相同和类型赋值
类型相同
- 命名类型:类型声明语句相同
- 未命名类型:声明字面量的结构相同,内部元素类型相同
- 命名类型和未命名类型永远不同
- 通过类型别名语句声明的两个类型相同。
Go 1.9 引入了 类型别名语法 type Tl = T2, Tl 的类型完全和 T2 一样。(先不考虑这种,感觉复杂)
类型赋值
var b T2 = a//a是T1类型
必须满足以下之一:
- T1,T2类型相同
- 底层类型相同,且至少一个是未命名类型
- T2是接口类型,T1是具体类型,T1的方法集是T2的方法集的超集
- 都是通道类型,且有相同元素,且有一个是未命名系统
- a是nil,T2是pointer, function, slice, map, channel, interface之一
- a是字面常量,可以表示T2的值
type Map map[string]string
//方法
func (m Map) Print(){
for _, key := range m{
fmt.Println(key)
}
}
type iMap Map
func (m iMap) Print(){
for _, key := range m{
fmt.Println(key)
}
}
type slice []int//没看懂
func (s slice) Print() {
for _, v := range s {
fmt.Println(v)
}
}
func main(){
mp :=make(map[string]string, 10)
mp["hi"] = "tata"
var ma Map = mp//符合第二条
ma.Print()
//var im iMap = ma //错误,第二条后半部分不符合
//ma赋值给接口类型变量,符合第三条
var i interface {
Print()
} = ma
i.Print()
s1 :=[]int{1,2,3}
var s2 slice
s2 = s1 //符合最后一条
s2.Print()
强制类型转换
var a T = (T)(b)
变量x转为T,满足条件之一
- x可以直接赋值给T
- x的类型和T具有相同的底层类型
- 都是未命名的指针类型,且指向的类型具有相同的底层类型
- 都是整型,或都是浮点型
- 都是复数类型
- x是整数值,或[]byte类型的值,T是string
- x是字符串,T是[]byte 或[]rune.
类型方法
为类型增加方法是Go 语言实现面向对象编程的基础。
自定义类型
格式
type newtype oldtype
newtype不会继承方法,和old有相同的底层结构,oldtype可以是任意类型(命名,未命名),newtype是命名类型中的自定义类型。
自定义类型值struct类型
是go语言面向对象承载的基础
用字面量表示的struct是未命名类型,用type声明是命名类型
//命名
struct XXXname struct{
filed1 type1
filed2 type2
}
//命名
type errorString struct{
s string
}
//未命名
struct{
filed1 type1
}
初始化
type Person struct{
name string
age int
}
多种初始化方法
//直接,不推荐,
a := Person{"admin",16}
b := Person{
"ada",
18,
}
c := Person{
"ada",
18}
//推荐
a := Person {name:"andes", age:18}
//使用new创建内置函数,字段默认初始化为零值,不推荐,返回指针?
p :=new(Person)
//不推荐,违背结构初衷
p :=Person{}
p.name="abc"
//var t =&T{}//这个也可以?
//用函数返回,推荐
func New(text string, age int) Person{
return Person{text,age}
}
结构字段可以是任意的类型,包括自身类型的指针,
type Element struct{
next,pre * Element
}
匿名字段
- 字段,只给出字段类型,没给出字段名,类型只能是命名类型或命名类型的指针。
- 此时字段名就是 指针指向的类型名,一个结构体不能包含某一类型及其指针类型的匿名字段
- 如果嵌入的字段雷子其他包,需要加上包名。
type File struct{
*file
}
自定义接口类型
var i interface{}//未命名类型变量
type Reader interface {//命名类型
Read(p []byte) (n int, err error)
}
方法
类型方法是对类型 行为的封装,第一个参数是对象实例或指针,称为接收者
//值类型
func (t TypeName) MethodName (ParamList ) (Returnlist) {
//method body
}
//指针
func (t *TypeName) MethodName (ParamList) (Returnlist) {
//method body
}
写成普通的函数,是等价的
func TypeName_MethodName(t TypeName, ParamList ) (Returnlist) {
//method body
}
//指针
func TypeName_MethodName(t *TypeName, ParamList ) (Returnlist) {
//method body
}
类型方法特点:
- 可以为命名类型增加方法(接口除外,因为接口本身就是一个方法的签名集合)非命名类型不可以
- 方法的定义和类型的定义在同一个包中,不能为预声明类型增加方法
- 方法的命名空间的可见性和变量一样,大写开头可以在包外被访问,否则包内可见
- type定义新类型不能继承方法,但是底层的运算可以继承,如map的for操作
方法调用
一般调用
TypeinstanceName.MethodName(ParamList)
p.getName()
方法值
M是方法,x.M是方法值,可以赋值给其他变量
type T struct{
a int
}
func (t T) Get() int{
return t.a
}
方法表达式
T.Get 和(*T).set
t : = T{a : l}
//等级
t.Get(t)//普通方法调用
(T).Get(t)//方法表达式调用
fl :=T.Get; fl(t)
f2 : = (T).Get ; f2 (t)
//如下方法表达式调用都是等价的
(*T) . Set (&t, 1)
f3 : = (*T) .Set; f3(&t , 1)
方法集
方法接收者recevier有两种类型,值类型,指针类型。
都是值拷贝:前者值传递,后者指针的值传递
一般来说,值传递用于获取,指针传递用于修改类型(结构)
type Int int
func (i *Int) set(a Int){
* i = a
}
func (i Int) Get() Int{
return i
}
但是调用不要求严格,系统会自动转换格式
c.Get()
(&c).Get()//会自动转换
c.set(19)//自动转换
(&c).set(99)
方法集总结如下:
将接收者( receiver )为值类型T 的方法的集合记录为S,将接收者( receiver)为指针类型 *T 的方法的集合统称为 *S,则:
类型T
的方法集为S
,
类型*T
的方法集为S
和*S
换句话说,*T可以调用所有方法
值调用和表达式调用的方法集
看不懂,略
组合和方法集
组合
type X struct{
a int
}
type Y struct{
X
a int
}
func main(){
x :=X{a:1}
y:=Y{X:x, a:2}
//Y.a=Y.X.a
可以多层嵌套,方法也一样,方法也存在覆盖,类似java子类覆盖父类的方法。
可以简化访问,最好不要同名,不然外层会覆盖内层
组合的方法集
若类型S
包含匿名字段T
,S
包含T
的方法集,如上面的Y包含X
若类型S
包含匿名字段*T
,S
包含T
*T
的方法集
*S
总包含T和*T
的方法集
type Y struct{
X
}
type Z struct{
*X
}
func main(){
x :=X{1}}
z:=Z{
X:&x}
z.Set(6)//自动转换
z.Set(z,4)//用方法表达式调用,防止自动转换
}
为什么要方法集?
后面学习接口的适合很有用。
假设方法要求指针类型,传递给接口变量的是值类型,接收到值会自动转换为指针,但是这个指针是副本的指针,不是我们希望的指针,所以不允许这种调用;反之是没问题的。
函数类型
函数类型分为两种:函数字面量类型(未命名),函数命名类型
函数字面量类型
包括有名函数和匿名函数。
函数命名类型
type NewFunType FuncLiteral
type ADD func (int, int) int
var g ADD = add
函数声明
调用Go语言编写的函数,不要声明
调用汇编,需要声明
//函数签名
func (Input)Output
func (int int) int
//声明
func FuncName (Input)Output
func add(int int) int