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

Go 语言的类型系统

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 main

import (
    "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 main

import (
    "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 main

import (
    "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 receiversvalues
(t T)T and *T
(t *T)*T

5.4.4 多态

// Sample program to show how polymorphic behavior with interfaces.
package main

import (
    "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 main

import (
    "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 或通过指定已存在的类型,可以声明用户定义的类型
  • 方法提供了一种给用户定义的类型增加行为的方式
  • 设计类型时需要确认类型的本质是原始的还是非原始的
  • 接口是声明一组行为并支持多态的类型
  • 嵌入类型提供了扩展类型的功能,而无需使用继承
  • 标识符要么是从包里公开的,要么是在包里未公开的
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值