3. 类型系统的学习

3.1 类型简介

3.1.1 命名类型和未命名类型

3.1.2 底层类型

3.1.3 类型相同和类型赋值

import (

    "fmt"

)

type Map map[string]string

func (m Map) Print() {

    for _, key := range m {

        fmt.Println(key)

    }

}

type iMap Map

//只要底层类型是slice、map等支持range的类型字面量,新类型仍然可以使用range迭代

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"

    // mp 与 ma 有相同的底层类型map[string]string, 并且mp是未命名类型

    // 所以mp可以直接赋值给ma

    var ma Map = mp

    // im 与 ma 虽然有相同的底层类型map[string]string,但它们中没有一个是未命名类型

    // 不能赋值,如下语句不能通过编译

    // var im iMap = ma

    ma.Print()

    im.Print()

    // Map实现了Print(),所以其可以赋值给接口类型变量

var i interface {

    Print()

}    = ma

i.Print()

s1 := []int {1, 2, 3}

var s2 slice

s2 = s1

s2.Print()

}

3.1.4 类型强制转换

非常量类型的变量x可以强制转换并传递给类型T,需要满足如下任一条件:

1)x可以直接赋值给T类型变量

2) x的类型和T具有相同的底层类型

继续使用上一节的示例:

type Map map[string]string

func (m Map) Print() {

    for _, key := range m {

        fmt.Println(key)

    }

}

type iMap Map

//只底层类型是slice 、 map等支持range类型字面量,行类型仍然可以使用ragen迭代

func (m iMap) Print()  {

    for _, key := range m {

        fmt.Println(key)

    }

}

func main() {

    mp := make(map[string]string, 10)

    mp["hi"] = "tata"

    // mp 与 ma 有相同的底层类型 map[string]string, 并且mp是未命名类型

    var ma Map = mp

    // im 与 ma 虽然有相同的底层类型,但是二者中没有一个是字面量类型,不能指直接赋值可强制进行类型转换

    // var im iMap = ma

    var  im iMap (iMap) (ma)

    ma.Print()

    im.Print()

}

3.2 类型方法

3.2.1 自定义类型

type newtype oldtype:  

oldtype是自定义类型、预声明类型、未命名类型中的任意一种。

newtype是新类型的标识符,与oldtype具有相同的底层类型,并且都继承了底层类型的操作集合

type INT int // INT是一个使用预声明类型声明的自定义类型

type Map map[string]string // Map 是一个使用类型字面量声明的自定义类型

type myMap Map // myMap 是一个自定义类型Map声明的自定义类型

// INT Map myMap 都是命名类型

自定义 struct 类型

//使用type自定义的结构类型属于命名类型

struct XXXXname struct {

    Field1 type1

    Field2 type2

}

// errorString 是一个自定义结构类型,也是命名类型

type errorString struct {

    s string

}

// 结构字面量属于未命名类型

struct {

    Field1 type1

    Field2 type2

}

// struct {} 是非命名类型空结构

var s = struct {} {}

struct初始化

以Person结构为例来讲一下结构的初始化的方法。例如:

type Person struct {

    name string

    age int

}

1)按照字段顺序进行初始化

//注意有三种写法

a := Person {"andes", 18}

b := Person {

    "andes",

    18,

}

c := Person{

    "andes", 18

}

2) 指定字段名进行初始化。例如:

a := Person { name: "andes", age : 18}

b := Person  {

    name: "andes",

    age:18,

}

c := Person {

    name: "andes",

    age:18

}

3)使用 new 创建内置函数,字段默认初始化为其类型的零值,返回值是指向结构的指针。例如:

p := new(Person) // 此时 name 为 "", age 是 0 ,  这种方法不常用,一般使用 struct都不会将所有字段初始化为零值

4)一次初始化一个字段。例如:

p := Person {}

p.name = "andes"

p.age  = 18 // 这种方法也不常用。

5)使用构造函数进行初始化

这是一种被推荐的方法,当结构放生变化,构造函数可以屏蔽细节。例如:

func New(text string) error {

    return &errorString { text }

}

type errorString struct {

    s string

}

结构字段的特点
匿名字段

type File struct {

    *file // os specific

}

自定义接口类型

// interface {} 是接口字面了量类型标识,所以 i 是非命名类型变量

var i interface {}

// Reader 是自定义接口类型,属于命名类型

type Reader interface {

    Read(p []byte) (n int, err error)

}

3.2.2 方法

3.3 方法调用

3.3.1 一般调用

3.3.2 方法值

3.3.3 方法表达式

// 如下方法表达式调用都是等价的

t := T { a :1}

t.Get(t)  // 普通方法调用

(T).Get(t) // 方法表达式调用

f1 := T.Get; f1(t) // 方法表达式调用

f2 := (T)..Get; f2(t)

//如下方法表达式调用都是等价的

(*T).Set(&t, 1)

f3 := (*T).Set; f3(&t, 1)

3.3.4 方法集

type Int it

func (a Int) Max(b Int) Int {

    if a >= b {

        return a

    } else {

        return b

    }

}

func (i *Int) Set(a Int) {

    *i = a

}

func (i Int) Print() {

    fmt.Printf("value=%d\n", i)

}

func main() {

    var a Int = 10

    var b Int = 20

    c := a.Max(b)

    c.Print() // value =50

    (&c).Print // value =50 ,内部被编译器转换为 c.Print()

    a.Set(20) // 内部被编译器转化为 (&a).Set(20)

    a.Print()  // value=20

    (&a).Set(30)

    a.Print()   // value=30

}

上面示例定义了一个新类型Int, 新类型的底层类型是int , Int虽然不能继承int的方法,但是底层类型支持的操作(算法运算和赋值运算)可以被上层类型继承。

3.3.5 值调用和表达式调用的方法集

3.4 组合和方法集

3.4.1 组合

内嵌字段的初始化和访问

type X struct {

    a int

}

type Y struct {

    X

    b int

}

type Z struct {

    Y

    c int

}

func main() {

    x := X {a:1}

    y := Y {

        X : x,

        b: 2,

    }

    z := Z {

        Y : y,

        c : 3,

    }

}

// z.a、 z.Y.a、 z.Y.X.a 三者是等价的, z.a z.Y.a 是 z.Y.X.a 的简写

内嵌字段的方法调用

在简写模式下,Go编译器优先从外向内逐层查找方法。同名方法中外层的方法能够覆盖内层的方法。这个特性有点类似面向对象,子类覆盖父类的同名方法。示例如下:

type X struct {

    a int

}

type Y struct {

    X

    b int

}

type Z struct {

    Y

    c int

}

func (x X) Print() {

    fmt.Printf("In X, a=%d\n", x.a)

}

func (x X) XPrint() {

   fmt.Printf("In X, a=%d\n", x.a)

 }

func (y Y) Print() {

    fmt.Printf("In Y, b=%d\n", y.b)

}

func (z Z) Print() {

    fmt.Printf("In Z, c=%d\n", z.c)

    // 显式的完全路径调用内嵌字段的方法

   z.Y.Print()

    z.Y.X.Print()

}

func main() {

    x := X { a:1}

    y := Y {

        X : x,

        b : 2,

    }

    z := Z {

        Y :y,

        c : 3,

    }

    // 从外向内查找,首先找到的是Z的Print()方法

    z.Print()

    // 从外向内查找,最后找到的是X 的 XPrint()方法

    z.XPrint()

    z.Y.XPrint()

}

3.4.2 组合的方法集

3.5 函数类型

函数类型分为: 函数字面量类型,函数命名类型

// 有名函数定义,函数名是add

// add 类型是函数字面量类型 func (int, int) int

func add(a, b int) int {

    return a + b

}

// 函数声明语句,用于Go代码调用汇编代码

func add(int, int) int

// 新定义函数类型ADD , ADD底层类型是函数字面量类型 func(int, int) int

type ADD func (int, int) int

// add 和 ADD 的底层类型相同,并且add是字面量类型,所以add可直接赋值给ADD类型的变量 g

var g ADD = add

func main() {

    f := func(a, b int) int {

        retutrn a + b

    }

    g(1, 2)

    f(1, 2)

}

两种类型的底层类型相同,并且其中一个是字面量类型,二者可以相互转换。

// src/net/http/server.go

// 定义一个有名函数类型HandlerFunc

type HandlerFunc func(ResponseWriter, *Request)

//为有名的函数类型添加方法,这是一种包装器的编程技法

// ServeHTTP calls f(w, r)

func (f HandlerFunc) ServerHTTP(w ResponseWriter, r *Request) {

    f(w, r)

}

// 函数类型HandlerFunc实现了接口Handler的方法

type Handler interface {

    ServerHTTP(ResponseWriter, *Request)

}

func (mux *ServerMux) Handle(parrern string, handler Handler)

//所以HandlerFunc类型的变量可以传递给Handler接口变量

func(mux *ServerMux) HandlerFunc(parrern string, handler func(ResponseWriter, *Request)) {

    mux.Handler(pattern, HandlerFunc(handler))

}

1)函数也是一种类型,可以在函数字面量类型的基础上定义一种命名函数类型.

2)有名函数和匿名函数的函数签名与命名函数类型的底层类型相同,他们之间可以进行类型转换。

3)可以为有名函数类型添加方法,这种为一个函数类型添加方法的技法非常有价值,可以方便的为一个函数“拦截”或者“过滤”等额外功能 ,这提供了一种装饰设计模式

4)为有名函数类型添加方法,使其与接口打通关系,使用接口的地方可以传递函数类型的变量,这为函数到接口的转换开启了大门。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值