类型系统 命名类型 & 未命名类型 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 package  mainimport  "fmt" type  Person struct  {    name string      age int  } func  main ()   {         a := struct  {         name string          age int      }{"andes" , 18 }     fmt.Printf("%T\n" , a)     fmt.Printf("%v\n" , a)          b := Person{"Tom" , 21 }     fmt.Printf("%T\n" , a)     fmt.Printf("%v\n" , a)      } 
 
底层类型 1 2 3 4 5 6 type  T1 string type  T2 T1type  T3 []string type  T4 T3type  T5 []T1type  T6 T5
 
其中,T1、T2 底层类型都是 string, T3、T4 底层类型都是[]string,T6 和 T5 的底层类型都是[]T1。特别注意是,这里T6、T5 和T3、T4的底层类型不一样,一个是[]T1,一个是[]string。
 
类型相同和类型赋值 Go 1.9 引入了类型别名语法 type T1 = T2 ,T1 的类型完全和 T2 一样。引入别名主要有如下原因:
未来解决新旧包的迁移兼容问题,比如 context 包先前并不在标准库里面,后面迁移到了标准库。 
Go 的按包进行隔离的机制不太精细,有时我们需要将大包划分为几个小包进行开发,但是需要在大包里面保留全部的类型给使用者。 
解决新旧类型的迁移问题,新类型先是旧类型的别名,后续的软件都基于类型编程,在合适的时间将新类型升级为旧类型不兼容,常用于软件的柔性升级。 
 
类型可直接赋值  
赋值需满足:
T1、T2类型相同 
T1和 T2具有相同的底层类型,并且T1、T2里面至少有一个是未命名类型 
T2 是接口类型,T1是具体类型,T1的方法集是T2方法集的超集 
T1、T2都是通道类型,他们拥有相同的元素类型,并且T1和T2中至少有一个是未命名类型 
a 是预声明标识符 nil,T2可以是pointer、function、slice、map、channel、interface类型中的一个 
a 是一个字面量值,可以用来标识类型 T 的值 
 
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 package  main import  (    "fmt"  ) type  Map map [string ]string func  (m Map)  Print ()   {    for  _, key := range  m {         fmt.Println(key)     } } type  iMap Mapfunc  (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()     im.Print()                    var  i interface  {         Print()     } = ma          i.Print()          s1 := []int [1 , 2 , 3 ]     var  s2 slice      s2 = s1     s2.Print() } 
 
类型强制转换 
x 可以直接赋值给 T 类型变量 
x 的类型和 T 具有相同的底层类型 
 
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 package  mainimport  (	"fmt"  ) type  Map map [string ]string func  (m Map)  Print ()   {    for  -, key := range  m {         fmt.Println(key)     } } type  iMap Mapfunc  (m iMap)  Print ()   {    for  _, key := range  m {         fmt.Println(key)     } } func  main ()   {    mp := make (map [string ]string , 10 )     mp["hi" ] = "tata"           var  ma Map = mp                         var  im iMap = (iMap) (ma)          ma.Print()     im.Print() } 
 
x 的类型、T 都是未命名指针类型,并且指针指向相同底层类型 
x、T 都是整值、或者都是浮点型 
x 的类型和 T 都是复数类型 
x 是整数值或 []byte 类型的值,T 是 string 类型 
x 是一个字符串,T 是 []byte 或 []rune 
 
字符串和字节切片之间的转换最常见,示例如下:
1 2 3 4 5 6 7 s := "hello,world"  var  a []byte a = []byte (s) var  b string b = string (a) var  c []rune c = []rune  
 
注意:
数值类型和 string 类型之间相互转换可能造成值部分丢失 
Go 语言没有语言机制支持指针和 interger 之间的直接转换 
 
 
类型方法 自定义类型 1 2 3 4 type  INT int  type  Map map [string ]string  type  myMap Map 
 
自定义 struct 类型 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 struct  Xname struct  {    Field1 type1 } type  errorString struct  {    s string  } struct  {    Field1 type1 } var  s = struct  {} {}
 
struct 初始化 1 2 3 4 type  Person struct  {    name string       age int  } 
 
推荐写法(指定字段名初始化) 1 2 3 4 5 6 7 8 9 10 a := Person{ name: "andes" , age:18  } b := Person{      name: "andes" ,      age:18 , } c b := Person{      name: "andes" ,      age:18 }  
 
使用构造函数进行初始化 推荐方法,当结构发生变化时,构造函数可以屏蔽细节。
1 2 3 4 5 6 7 func  New (text string )  error   {    return  & errorString{text} } type  errorString struct  {    s string  } 
 
结构字段的特点 结构的字段可以是任意类型,结构字段的类型名必须唯一, struct 字段类型可以是普通类型也可以是指针。另外结构支持内嵌自身的指针。
1 2 3 4 5 type  Element struct  {    next, prev *Element     list *List     Value interface {} } 
 
匿名字段 1 2 3 type  File struct  {    *file  } 
 
自定义接口类型 1 2 3 4 var  i interface {}type  Reader interface  {    Read(p []byte ) (n int , err error) } 
 
方法 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 func  (t TypeName)  MethodName  (ParamList)  (Return List)   {     } func  (t *TypeName)  MethodName  (ParamList)  (Return List)   {     } func  TypeName_MethodName (t TypeName, otherParamList)  (Return List)   {     } func  TypeName_MethodName (t *TypeName, otherParamList)  (Return List)   {     } type  SliceInt []int func  (s SliceInt)  Sum ()  int   {    sum := 0       for  _, i := range  s {         sum += i     }     return  sum } func  SliceInt_Sum (s SliceInt)  int   {    sum := 0      for  _, i := range  s {         sum += i     }     return  sum } var  s SliceInt = []int {1 , 2 , 3 , 4 }s.Sum() SliceInt_Sum(s) 
 
类型方法有如下特点:
可以为命名类型增加方法(除了接口),非命名类型不能自定义方法。 
为类型增加方法有一个限制,方法定义必须和类型定义在同一个包中。 
方法的命名空间的可见性和变量一样,大写开头的方法可以在包外被访问,否则只能在保内可见。 
使用 type 定义的自定义类型是一个新类型,新类型不能调用原有类型方法,但是底层类型支持的运算可以被新类型继承。 
 
 
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 type  Map map [string ]string func  (m Map)  Print ()   {    for  _, key := range  m {         fmt.Println(key)     } } type  MyInt int  func  main ()   {    var  a MyInt = 10      var  b MyInt = 10                c := a + b     d := a * b } 
 
方法调用 一般调用 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 type  T struct  {    a int  } func  (t T)  Get ()  int   {    return  t.a } func  (t *T)  Set (i int )   {    t.a = i } var  t = &T{}t.Set(2 ) t.Get() 
 
方法值 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 type  T struct  {    a int  } func  (t T)  Get ()  int   {    return  t.a } func  (t *T)  Set (i int )   {    t.a = i } func  (t *T)  Print ()   {    fmt.Printf("%p, %v, %d \n" , t, t, t.a) } var  t = &T{}f := t.Set f(2 ) t.Print() f(3 ) t.Print() 
 
方法表达式 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 type  T struct  {    a int  } func  (t T)  Get ()  int   {    return  t.a } func  (t *T)  Set (i int )   {    t.a = i } func  (t *T)  Print ()   {    fmt.Printf("%p, %v, %d \n" , t, t, t.a) } 
 
表达式 T.Get() (T).Set 被称之为方法表达式,方法表达式也可以被看做函数名,只不过这个函数的首个参数是接收者的实例活指针。 T.Get 的函数签名是 func(t T) int,  (\ T).Set 的签名是 func(t *T, i int)。 注意这里是T.Get不能写成 (*T).Get,同理 (*T).Set不能写成 T.Set。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 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 f(&t, 1 ) 
 
方法集 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 package  mainimport  "fmt" type  Int int 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()     (&c).Print()          a.Set(10 )      a.Print()          (&a).Set(30 )     a.Print() } 
 
值调用和表达式调用的方法集 
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 type  Date struct  {}func  (Data)  TestValue ()   {}func  (*Data)  TestPointer  ()   {}(*Data) (&struct  {} {}).TestPointer() (*Data) (&struct  {} {}).TestValue (Data) (struct  {} {}).TestValue () Data.TestValue(struct  {} {}) 
 
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 type  Data struct  {}func  (Data)  TestValue ()   {}func  (*Data)  TestPointer  ()   {}var  a Data = struct  {}{}Data.TestValue(a) (*Data).TestPointer(&a) f := a.TestValue f() y := (&a).TestValue  y() g := a.TestPointer g() x := (&a).TestPointer x() 
 
组合和方法集 组合 
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 package  maintype  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 ,     }          println (z.a, z.Y.a, z.Y.X.a)      z = Z{}     z.a = 2      println (z.a, z.Y.a, z.Y.X.a)  } 
 
嵌套层次可以有相同字段,但是最好避免,以免出现歧义
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 package  maintype  X struct  {    a int  } type  Y struct  {    X     a int  } type  Z struct  {    Y     a int  } func  main ()   {    x := X{a: 1 }     y := Y{         X: x,         a: 2 ,     }     z := Z{         Y: y,         a: 3 ,     }          println (z.a, z.Y.a, z.Y.X.a)      z = Z{}     z.a = 4      z.Y.a = 5      z.Y.X.a = 6      println (z.a, z.Y.a, z.Y.X.a)  } 
 
内嵌字段的方法调用 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 package  mainimport  "fmt" 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.XPrint()     z.Y.XPrint() } 
 
不推荐在多层的 struct 类型中内嵌多个同名的字段;但是并不反对 struct 定义和内嵌字段同名方法的用法,因为这提供了一种一种编程技术,使得 struct 能够重写内嵌字段的方法,提供面向对象编程中子类覆盖父类的同名方法的功能。
 
组合的方法集 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 package  maintype  X struct  {    a int  } type  Y struct  {    X } type  Z struct  {    *X } func  (x X)  Get ()  int   {    return  x.a } func  (x *X)  Set (i int )   {    x.a = i } func  main ()   {    x := X{a: 1 }     y := Y{         X: x,     }     println (y.Get())           y.Set(2 )     println (y.Get())               (*Y).Set(&y, 3 )                    println (y.Get())      z := Z{         X: &x,     }                         Z.Set(z, 4 )     println (z.Get())      (*Z).Set(&z, 5 )     println (z.Get())  } 
 
编译器的自动转换仅适用于直接通过类型实例调用方法时才有效,类型实例传递给接口时,编辑器不会进行自动转换,而是会进行严格的方法集校验。
 
函数类型 
1 2 type  NewFuncType FuncLiteraltype  NewFuncType OldFuncType
 
Go 语言函数不需要声明,可以直接调用,但是Go调用汇编语言编写的函数还是要使用函数声明语句
1 2 3 4 5 6 func  (InputTypeList)  OutputTypeList // 函数声明 func  FuncName  (InputTypeList)  OutputTypeList 
 
示例:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 func  add (a, b int )  int   {    return  a + b } func  add (int , int )  int // add  函数的签名,实际上就是 add  的字面量类型 func  (int , int )  int // 匿名函数不能独立存在,常作为函数参数、返回值或者赋值给某个变量 // 匿名函数可以直接显式初始化 // 匿名函数的类型也是函数字面量类型 func  (int , int )  int  func  (a, b int )  int   {    return  a + b } type  ADD func  (int , int )  int // add  和 ADD  的类型相同,并且 add  是字面量类型 // 所以 add  可直接赋值给 ADD  类型的变量 g  var  g  ADD  = add func  main ()   {    f := func  (a, b int )  int   {         return  a + b     }     g(1 , 2 )     f(1 , 2 )               fmt.Printf("%T\n" , f)      fmt.Printf("%T\n" , add)   } 
 
HTTP 标准库范例:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 type  HandlerFunc func  (ResponseWriter, *Request) // 为有名函数类型添加方法 // 这是一种包装器的编程技法 // ServerHTTP  calls  f (w, r)  func  (f HandlerFunc)  ServerHTTP (w ResponseWriter, r *Request)  {    f(w, r) } type  Handler interface  {    ServerHTTP(ResponseWriter, *Request) } func  (mux *ServeMux)  Handle (patterm string , handler Handler) // 所以 HanderFunc  类型的变量可以传递给 Handler  接口变量 func  (mux *ServeMux)  HandleFunc (patterm string , handler func (ResponseWriter, *Request) ) {    mux.Handle(pattern, HandlerFunc(handler)) }