Go语言规格说明书 之 类型(Types)
go version go1.11 windows/amd64
本文为阅读Go语言中文官网的规则说明书(https://golang.google.cn/ref/spec)而做的笔记,完整的介绍Go语言的 类型(Types)。
官文的 类型 的 目录结构 如下(12项):
Types
-Method sets
-Boolean types
-Numeric types
-String types
-Array types
-Slice types
-Struct types
-Pointer types
-Function types
-Interface types
-Map types
-Channel types
其中一些类型自己是熟悉的,但Go语言中特有的类型就不那么清楚了,比如,Channel types,第一次见到这种类型,也是重难点;Method sets的概念也是第一次见,好像和Interface types有些关系,需dig清楚;而Interface types也可以Java中的概念不一样;
其它的,Slice types在Python有所耳闻,但在Go里面是一个专门的类型;Struct、Pointer types的概念在C/C++中见到过,没想到这里又出现了。
审查后发现,总共12项,其中6项需要花精时弄清楚才行。
下面分别介绍(添加了一个type (...),出现在官文Types一节的开始位置,也是重难点):
-type (...)
这部分还不是很清楚,官文强翻(译)如下:
Go预定义了一些类型名称,其它类型则需要type关键字来声明。组合类型——比如,数组、结构体、指针、函数、接口、分片、映射和通道类型等——可以使用类型常量构建。
每一个类型 T 都有一个 底层类型:...(后面内容还是看官文吧!)
官文给的示例:
type ( A1 = string A2 = A1 ) type ( B1 string B2 B1 B3 []B1 B4 B3 )
上面的string、A1、A2、B1、B2的 底层类型 是 string,[]B1、B3、B4的底层类型是[]B1(我以为是 []string)。
下面是自己的测试,结果表明,上面的语句就是 定义类型别名 使用的嘛!
package main import ( "reflect" "fmt" ) type ( A1 = string A2 = A1 ) type ( B1 string B2 B1 B3 []B1 B4 B3 ) func main() { var str1 A1 = "1234" var str2 A2 = "abcd" fmt.Println("----测试1----") fmt.Println(str1) fmt.Println(str2) fmt.Println() fmt.Println(typeof(str1)) fmt.Println(typeof(str2)) fmt.Println() fmt.Println(typeof2(str1)) fmt.Println(typeof2(str2)) fmt.Println("----测试2----") var str3 B1 = "haha" var str4 B2 = "heihei" var strs1 B3 strs1 = append(strs1, "dad", "mom") var strs2 B4 strs2 = append(strs2, "tom", "jerry") fmt.Println(str3) fmt.Println(str4) fmt.Println(strs1) fmt.Println(strs2) fmt.Println() fmt.Println(typeof(str3)) fmt.Println(typeof(str4)) fmt.Println(typeof(strs1)) fmt.Println(typeof(strs2)) fmt.Println() fmt.Println(typeof2(str3)) fmt.Println(typeof2(str4)) fmt.Println(typeof2(strs1)) fmt.Println(typeof2(strs2)) } // https://studygolang.com/articles/10524 func typeof(v interface{}) string { return fmt.Sprintf("%T", v) } func typeof2(v interface{}) string { return reflect.TypeOf(v).String() }
测试结果:
----测试1---- 1234 abcd string string string string ----测试2---- haha heihei [dad mom] [tom jerry] main.B1 main.B2 main.B3 main.B4 main.B1 main.B2 main.B3 main.B4
说明,虽然进行了测试,可是,自己对这个用法还是不太熟悉,什么时候会用到了?还需要dig!
-Method sets
方法集。
一个类型可以拥有一个方法集。一个实现了接口类型的方法集合的类型 也是这个 接口类型(前面学习的经验)。
官文后面的就不清楚什么意思了,什么receiver type T、receiver *T……
在方法集合中,每一个方法都是一个 唯一的、非空的 方法名称。
可以看下面的示例(来自RUNOOB.COM)(下面的示例包含两份代码,简单的部分被注释掉了):
package main import ( "fmt" ) //type Phone interface { // call() //} // //type NokiaPhone struct { // //} // //func (nokiaPhone NokiaPhone) call() { // fmt.Println("I am Nokia, I can all you") //} // //type IPhone struct { // //} // //func (iPhone IPhone) call() { // fmt.Println("I am iPhone, I can call you") //} // 给接口增加参数 type Man interface { name() string age() int } type Woman struct { } func (woman Woman) name() string { return "Jin Yawei" } func (woman Woman) age() int { return 23 } type Men struct { } func (men Men) name() string { return "liweibin" } func (men Men) age() int { return 27 } func main() { // var phone Phone // // phone = new(NokiaPhone) // phone.call() // // phone = new(IPhone) // phone.call() // 给接口增加参数 var man Man man = new(Woman) fmt.Println(man.name(), man.age()) man = new(Men) fmt.Println(man.name(), man.age()) }
运行结果:
Jin Yawei 23 liweibin 27
说明&思考,关于方法集的更多内容,可以查看struct类型相关文档。通过 方法集、interface 这两个概念,Go语言将 数据、使用数据的方法 彻底分开了。还需要dig!
-Boolean types
布尔类型,类型名称为 bool,两个字面量:true、false。
-Numeric types
数值类型。
整数 和 浮点数 和 复数,包括:
预定义 的和计算机架构无关的数值类型如下(官文):
uint8 the set of all unsigned 8-bit integers (0 to 255) uint16 the set of all unsigned 16-bit integers (0 to 65535) uint32 the set of all unsigned 32-bit integers (0 to 4294967295) uint64 the set of all unsigned 64-bit integers (0 to 18446744073709551615) int8 the set of all signed 8-bit integers (-128 to 127) int16 the set of all signed 16-bit integers (-32768 to 32767) int32 the set of all signed 32-bit integers (-2147483648 to 2147483647) int64 the set of all signed 64-bit integers (-9223372036854775808 to 9223372036854775807) float32 the set of all IEEE-754 32-bit floating-point numbers float64 the set of all IEEE-754 64-bit floating-point numbers complex64 the set of all complex numbers with float32 real and imaginary parts complex128 the set of all complex numbers with float64 real and imaginary parts byte alias for uint8 rune alias for int32
还有下面几个 预定义 的数值类型,但它们和计算机架构有关系:
uint either 32 or 64 bits int same size as uint uintptr an unsigned integer large enough to store the uninterpreted bits of a pointer value
官文:为了避免移植问题, 所有的数值类型都是 定义了的类型(defined types?)并且是清楚的,除了其中的byte——uint8的别名、rune——int32的别名。不同的数值类型参与同一个计算时,需要显示转换,Go语言不存在隐式转换——这和之前遇到的编程语言不同。比如,int32 和 int 是不同类型,即便它们在某些架构上有相同的字节数。
-String types
字符串类型。
不可改变。是一个 defined types。
字符串长度获取:使用内建函数 len。若字符串是常量,那么,len获取的值就是编译时常量(compile-time constant)。
字符串的 字符 (官文是bytes,感觉有错误)可以通过下标 0 到 len(s) -1获取。
注意,获取一个字符串中字符的地址 是错误的,即 &s[i] 是非法的!
-Array types
数组类型。
数组中的元素类型是 相同的,数组元素的个数 即为 数组的长度,数组的长度不为负数。
和字符串类型语言获取数组类型长度 使用内建函数len。
官文中这句不太理解: Array types are always one-dimensional but may be composed to form multi-dimensional types.
翻译:数组类型都是一维的,但可以组合成多维类型。
就是说,检测到的都是数组类型,但是呢,有的是一维的、有的是二维的?
测试数组类型:
var arr1 [2]int var arr2 [2][2]int fmt.Println(typeof(arr1)) fmt.Println(typeof(arr2)) fmt.Println(typeof2(arr1)) fmt.Println(typeof2(arr2)) // https://studygolang.com/articles/10524 func typeof(v interface{}) string { return fmt.Sprintf("%T", v) } func typeof2(v interface{}) string { return reflect.TypeOf(v).String() }
测试结果:
[2]int [2][2]int [2]int [2][2]int
二维数组检测到就是二维的啊?官文有误?
-Slice types
切片类型。
参考链接:Go 语言切片(Slice) by RUNOOB.COM,里面有更清楚的介绍。
切片类型是对 数组 的抽象(an underlying array),一个未初始化的切片类型的值为 nil。
通过 len函数 获取切片长度,通过 cap函数 获取切片容量。
和数组一样,切片中的数据类型是相同的,一维的切片 可以 组合成 多维的切片。
-Struct types
结构体类型。
是一系列被称为 域 的 命名元素 的 序列,每一个域都有一个名称和类型。
域的名称可以显示或隐式地制定。(疑惑)
在结构体中,非空的域的名称必须是唯一的,也就是说,域的名称可以为空(下划线,_)。
官文示例如下:
// An empty struct.
struct {}
// A struct with 6 fields.
struct {
x, y int
u float32
_ float32 // padding
A *[]int
F func()
}
一个声明了类型但没有指定域名称的域被叫做 嵌入域(embedded field),……(更多翻译省略,还有些复杂)。
还有 promoted fields,promoted methods,tag……(不太清楚)。
这个结构体类型还需要dig才是!
-Pointer types
指针类型。
指的是 所有给定类型的变量的指针的集合,被称为指针的基本类型(zzzz)。
未初始化 的指针 是 nil。
取地址 运算符:&;指针变量:*。
-Function types
函数类型。
函数成为了类型,这就是Go支持函数式编程的接触吧!
一个函数类型 指的是 具有相同参数、返回值 的函数的集合。
未初始化 的函数 是 nil。
关键字 func。
函数的参数的名称可以存在,也可以不存在。
参数 和 返回结果 列表必须加 圆括号,除非有一个没有命名的结果(zzzz)。
注意,函数的最后一个参数 可以用 三个点(...)做前缀,这便是此参数为 可变参数,可以 拥有 0到多个 元素。
官文的函数类型示例:
func() func(x int) int func(a, _ int, z float32) bool func(a, b int, z float32) (bool) func(prefix string, values ...int) func(a, b int, z float64, opt ...interface{}) (success bool) func(int, int, float64) (float64, *[]int) func(n int) func(p *T)
疑问,同一作用域下函数的名称(不是签名)是否可以相同?但参数、返回类型不同?
-Interface types
接口类型。
和Java等面向对象中的接口不是同一个概念。
它是 一个 方法的集合。
未初始化 的接口类型 是 nil。
一个类型,其包含的所有方法 完全涵盖了 某接口类型 中定义的方法,那么,就称 这个类型实现了这个接口。
接口类型中的方法名称是唯一的。
接口嵌入:把另一个接口的类型名称 放到 当前接口类型的定义中,实现嵌入。嵌入 也需要保证 方法名的 唯一性。
type Locker interface { Lock() Unlock() } type ReadWriter interface { Read(b Buffer) bool Write(b Buffer) bool } type File interface { ReadWriter // same as adding the methods of ReadWriter Locker // same as adding the methods of Locker Close() } type LockedFile interface { Locker File // illegal: Lock, Unlock not unique Lock() // illegal: Lock not unique }
注意:禁止嵌入 自身,禁止出现 环状嵌入——你嵌入我、我嵌入你。
-Map types
映射类型(键值对)。
无序的元素集合,由键值对注册,键有唯一性要求,键和值得类型是确定的。
未初始化 的映射类型 是 nil。
键类型的要求:==、!= 运算符要实现!因此,键类型不能是函数、分片、映射类型。可以是 接口类型,但是,这些运算符需要为这些动态键值定义,失败则返回run-time panic。
map[string]int map[*T]struct{ x, y float64 } map[string]interface{}
映射类型有 长度,用 len函数 获取。
程序运行时可以 赋值,使用 键值 获取值,使用 delete函数 删除值。
映射类型 也有 容量的概念,但不是限制值。
注意,nil 映射 和 空映射相等,除非,没有元素被添加(原文:A nil
map is equivalent to an empty map except that no elements may be added.)(感觉自己这翻译有问题啊,还是 官文有问题?)。
-Channel types
通道类型。
Go语言中最特别的类型了,用于支持并发等应用,重难点啊!
能熟练使用了,就算是熟悉Go开发了!
官文:
通道提供了实现并发执行函数的机制——通过发送和接受指定元素类型的值。未初始化的通道 是 nil。
可选的 运算符 <- 指明了通道的方向:发送、接收。若是没有给出,这个通道就是 双向的,又可以发送 又可以接收
chan T // can be used to send and receive values of type T chan<- float64 // can only be used to send float64s <-chan int // can only be used to receive ints
下面有个更复杂的用法:通道串联。leftmost什么的。直接看示例吧。(自己需要搞清楚)
chan<- chan int // same as chan<- (chan int) chan<- <-chan int // same as chan<- (<-chan int) <-chan <-chan int // same as <-chan (<-chan int) chan (<-chan int)
其它:
建立新的初始化的 通道 可以使用 内建函数make,还可以指定 容量(官文有更详细的解释)。
nil 通道不能被用于通信。
通道可以使用内建函数 close 关闭(上午试验了一个程序,结果发生了一些问题)。
官文后面 又和 goroutine 扯上关系了,不懂啊,暂时就这样了!
--------翻篇--------
好了,就这样了,大半个下午就在这片博文中过去了!对于Go语言的类型,自己 熟悉 多少了呢?使用程度又怎样呢?尤其是它的一些重难点?
后面会写 表达式、语句,再结合本文,应该会更好理解吧!
后续,Go开发水平进步了,一定要对这篇文章进行补充!因为目前这篇文章,最多得59分吧,离及格还差那么一点点啊!