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