go 类型系统

数据类型:

  • 简单类型:布尔 整型 浮点型 复数(字符) 字符串
  • 复杂类型:数组 切片 字典 结构 指针 函数类型

类型系统分为:

  • 命名系统
  • 非命名系统
  • 底层类型
  • 动态类型
  • 静态类型

自定义类型,类型方法

类型简介

命名类型 未命名类型

命名类型
类型可以通过标识符表示。包括:

  1. 20个预声明简单类型
    如:int int8 int16 int32 int64
    uint uint8 uint16 uint32 uint64 uintptr
    float32 float64 complex128 complex64
    bool byte rune string error
    https://blog.csdn.net/yulijia/article/details/81738479
  2. 自定义类型: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类型

必须满足以下之一:

  1. T1,T2类型相同
  2. 底层类型相同,且至少一个是未命名类型
  3. T2是接口类型,T1是具体类型,T1的方法集是T2的方法集的超集
  4. 都是通道类型,且有相同元素,且有一个是未命名系统
  5. a是nil,T2是pointer, function, slice, map, channel, interface之一
  6. 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,满足条件之一

  1. x可以直接赋值给T
  2. x的类型和T具有相同的底层类型
  3. 都是未命名的指针类型,且指向的类型具有相同的底层类型
  4. 都是整型,或都是浮点型
  5. 都是复数类型
  6. x是整数值,或[]byte类型的值,T是string
  7. 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
}

类型方法特点:

  1. 可以为命名类型增加方法(接口除外,因为接口本身就是一个方法的签名集合)非命名类型不可以
  2. 方法的定义和类型的定义在同一个包中,不能为预声明类型增加方法
  3. 方法的命名空间的可见性和变量一样,大写开头可以在包外被访问,否则包内可见
  4. 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包含匿名字段TS包含T的方法集,如上面的Y包含X
若类型S包含匿名字段*TS包含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
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值