• 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()
    }
    type (...)测试

    测试结果:

    ----测试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
    type (...)测试结果

    说明,虽然进行了测试,可是,自己对这个用法还是不太熟悉,什么时候会用到了?还需要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
    预定义数值类型(2)

    官文:为了避免移植问题, 所有的数值类型都是 定义了的类型(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分吧,离及格还差那么一点点啊!

  • 相关阅读:
    Oracle导出txt文本文件
    oracle spool
    [Oracle, MySQL] Oracle通过dblink连接MySQL
    正则表达式速查表
    常用的正则表达式
    python3 打印九九乘法口诀表
    canda 常用命令
    python3 拼接字符串的7种方法
    Python 字符串格式化输出方式
    PyCharm 解决有些库(函数)没有代码提示
  • 原文地址:https://www.cnblogs.com/luo630/p/9636139.html
Copyright © 2020-2023  润新知