5. Go 语言的类型系统(Go Tutorial)

来源:互联网 发布:同步带传动计算软件 编辑:程序博客网 时间:2024/06/05 16:42

Go 语言的类型系统

  • 1 用户定义的类型
    • 11 使用 struct 关键字声明结构类型
    • 12 基于一个已有类型将其作为新类型的说明
  • 2 方法
  • 3 类型的本质
    • 31 内置类型
    • 32 引用类型
    • 33 结构类型
  • 4 接口
    • 41 标准库
    • 42 实现
    • 43 方法集
    • 44 多态
  • 5 嵌入类型
  • 6 公开或未公开的标识符
  • 7 小结

Go 语言是一种静态类型的编程语言,这意味着编译器在编译时需要事先知道变量的类型,这样有助于编译器对代码的一些优化,提高执行效率。

5.1 用户定义的类型

Go 语言里声明用户自定义类型有两种方式:

  • 使用关键字 struct 创建结构类型
  • 基于一个已有类型,将其作为新类型的类型说明

5.1.1 使用 struct 关键字声明结构类型

声明一个结构类型

type user struct {  name string  email string  age int  isMale bool}

使用结构类型声明变量并初始化零值

var bill user

任何时候,创建一个变量并初始化为零值,习惯上是使用关键字 var。如果变量被初始化为非零值,就配合结构字面量和短变量声明操作符来创建变量(即 := )

使用结构字面量来声明一个结构类型的变量

lisa := user{  name: "lisa",  email: "lisa@email.com"  age: 22,  isMale: false,}

不使用字段名,创建结构类型的值

// 这种形式下,值的顺序很重要,必须要和结构声明中的顺序一致,另外最后也不需要使用 ',' 结尾// 可以部分初始化,未初始化的部分为零值lisa := {"lias", "lisa@eamil.com", 22, false}

使用其他结构类型声明字段

type admin struct {  person user  level string}

使用结构字面量来创建字段的值

fred := admin {  person: user {    name: "fred",    email: "fred@email.com"    age: 22,    isMale: true,  },  level: "super",}

5.1.2 基于一个已有类型,将其作为新类型的说明

当需要一个可以用已有类型表示的新类型的时候,这种方法会很好用。

基于 int64 声明一个新类型

type Duration int64

5.2 方法

方法能给用户定义的类型添加新的行为。

方法实际上也是函数,只是在声明时,在关键字 func 和方法名之间增加了一个参数。

// Sample program to show how to declare methods and how the Go// compiler supports them.package mainimport (    "fmt")// user defines a user in the program.type user struct {    name  string    email string}// notify implements a method with a value receiver.func (u user) notify() {    fmt.Printf("Sending User Email To %s<%s>\n",        u.name,        u.email)}// changeEmail implements a method with a pointer receiver.func (u *user) changeEmail(email string) {    u.email = email}// main is the entry point for the application.func main() {    // Values of type user can be used to call methods    // declared with a value receiver.    bill := user{"Bill", "bill@email.com"}    bill.notify()    // Pointers of type user can also be used to call methods    // declared with a value receiver.    lisa := &user{"Lisa", "lisa@email.com"}    lisa.notify()    // Values of type user can be used to call methods    // declared with a pointer receiver.    bill.changeEmail("bill@newdomain.com")    bill.notify()    // Pointers of type user can be used to call methods    // declared with a pointer receiver.    lisa.changeEmail("lisa@newdomain.com")    lisa.notify()}

5.3 类型的本质

类型的本质主要体现在,如果给这个类型增加或删除某个值,是创建一个新值,还是在更改当前值。这也关系到这个类型在函数之间到底时用值传递还是指针传递。

5.3.1 内置类型

内置类型时语言提供的一组类型,比如数值类型、字符串类型、布尔类型和数组等。

这些类型本质上是原始类型,在函数或方法间传递时,传递的是对应值的副本

5.3.2 引用类型

Go 语言里的引用类型有如下几个:切片、映射、通道、接口和函数类型。

这些类型在函数间传递的是指针,共享底层数据结构。

5.3.3 结构类型

结构类型可以用来描述一组数据值,这组值的本质既可以是原始的,也可以是非原始的。

5.4 接口

多态是指代码可以根据类型的具体实现采取不同的行为。

如果一个类型实现了某个接口,所有使用这个接口的地方,都可以支持这种类型的值。

5.4.1 标准库

// Sample program to show how to write a simple version of curl using// the io.Reader and io.Writer interface support.package mainimport (    "fmt"    "io"    "net/http"    "os")// init is called before main.func init() {    if len(os.Args) != 2 {        fmt.Println("Usage: ./example2 <url>")        os.Exit(-1)    }}// main is the entry point for the application.func main() {    // Get a response from the web server.    r, err := http.Get(os.Args[1])    if err != nil {        fmt.Println(err)        return    }    // Copies from the Body to Stdout.    io.Copy(os.Stdout, r.Body)    if err := r.Body.Close(); err != nil {        fmt.Println(err)    }}

5.4.2 实现

接口是用来定义类型的行为。这些被定i的行为不由接口直接实现,而是通过方法由用户定义的类型实现。如果用户定义的类型实现了接口的一组方法之后,那么这个用户定义的类型的值就可以赋给这个接口类型的值。

在这种关系里,用户定义的类型通常称为实体类型。

5.4.3 方法集

方法集定义了接口的接收规则。

// Sample program to show how to use an interface in Go.package mainimport (    "fmt")// notifier is an interface that defined notification// type behavior.type notifier interface {    notify()}// user defines a user in the program.type user struct {    name  string    email string}// notify implements a method with a pointer receiver.func (u *user) notify() {    fmt.Printf("Sending user email to %s<%s>\n",        u.name,        u.email)}// main is the entry point for the application.func main() {    // Create a value of type User and send a notification.    u := user{"Bill", "bill@email.com"}    sendNotification(u)    // ./listing36.go:32: cannot use u (type user) as type    //                     notifier in argument to sendNotification:    //   user does not implement notifier    //                          (notify method has pointer receiver)}// sendNotification accepts values that implement the notifier// interface and sends notifications.func sendNotification(n notifier) {    n.notify()}

上述代码编译不通过的原因在于:

  • 如果使用指针接收者来实现了一个接口,那么只有指向那个类型的指针才能够实现对应的接口
  • 如果使用值接收者来实现一个接口,那么那个类型的值和指针都能够实现对应的接口

从方法接收者类型的角度来看方法集

methods receivers values (t T) T and *T (t *T) *T

5.4.4 多态

// Sample program to show how polymorphic behavior with interfaces.package mainimport (    "fmt")// notifier is an interface that defines notification// type behavior.type notifier interface {    notify()}// user defines a user in the program.type user struct {    name  string    email string}// notify implements the notifier interface with a pointer receiver.func (u *user) notify() {    fmt.Printf("Sending user email to %s<%s>\n",        u.name,        u.email)}// admin defines a admin in the program.type admin struct {    name  string    email string}// notify implements the notifier interface with a pointer receiver.func (a *admin) notify() {    fmt.Printf("Sending admin email to %s<%s>\n",        a.name,        a.email)}// main is the entry point for the application.func main() {    // Create a user value and pass it to sendNotification.    bill := user{"Bill", "bill@email.com"}    sendNotification(&bill)    // Create an admin value and pass it to sendNotification.    lisa := admin{"Lisa", "lisa@email.com"}    sendNotification(&lisa)}// sendNotification accepts values that implement the notifier// interface and sends notifications.func sendNotification(n notifier) {    n.notify()}

5.5 嵌入类型

嵌入类型是将已有的类型直接声明在新的结构里,从而实现扩展或修改已有类型的行为。被嵌入的类型被称为新的外部类型的内部类型。

要嵌入一个类型,只需要在外部类型中声明之歌类型的名字就可以了。

内部类型的值以及实现的接口都会自动的提升到外部类型,但当外部类型也有相同的值或者实现了相同的结构时,内部类型将不会提升,但此时仍可以通过内部类型的标识来访问。

// Sample program to show what happens when the outer and inner// type implement the same interface.package mainimport (    "fmt")// notifier is an interface that defined notification// type behavior.type notifier interface {    notify()}type person struct {    age int    gender string}// user defines a user in the program.type user struct {    person    name  string    email string}// notify implements a method that can be called via// a value of type user.func (u *user) notify() {    fmt.Printf("Sending user email to %s<%s>\n",        u.name,        u.email)}// admin represents an admin user with privileges.type admin struct {    user    level string    age int}// notify implements a method that can be called via// a value of type Admin.func (a *admin) notify() {    fmt.Printf("Sending admin email to %s<%s>\n",        a.name,        a.email)}// main is the entry point for the application.func main() {    // Create an admin user.    ad := admin{        user: user{            name:  "john smith",            email: "john@yahoo.com",            person: person{                age: 24,                gender: "male",            },        },        level: "super",        age: 26,    }    // Send the admin user a notification.    // The embedded inner type's implementation of the    // interface is NOT "promoted" to the outer type.    sendNotification(&ad)    // We can access the inner type's method directly.    ad.user.notify()    // The inner type's method is NOT promoted.    ad.notify()    fmt.Println(ad.user.person.gender)    fmt.Println(ad.person.gender)    fmt.Println(ad.gender)    fmt.Println(ad.user.person.age)    fmt.Println(ad.person.age)    fmt.Println(ad.age)}// sendNotification accepts values that implement the notifier// interface and sends notifications.func sendNotification(n notifier) {    n.notify()}// Sending admin email to john smith<john@yahoo.com>// Sending user email to john smith<john@yahoo.com>// Sending admin email to john smith<john@yahoo.com>// male// male// male// 24// 24// 26

5.6 公开或未公开的标识符

Go 语言支持公开或隐藏标识符。

当一个标识符的名字以小写字母开头,那么这个标识符就是未公开的,在包外不可见

当一个标识符的名字以大写字母开头,那么这个标识符就是公开的,在包外可见

5.7 小结

  • 使用关键字 struct 或通过指定已存在的类型,可以声明用户定义的类型
  • 方法提供了一种给用户定义的类型增加行为的方式
  • 设计类型时需要确认类型的本质是原始的还是非原始的
  • 接口是声明一组行为并支持多态的类型
  • 嵌入类型提供了扩展类型的功能,而无需使用继承
  • 标识符要么是从包里公开的,要么是在包里未公开的
原创粉丝点击