Go语言类型系统
用户定义的类型
- 使用结构类型变量,并初始化为其零值
// 声明user类型的变量
var bill user
当声明变量时,这个变量对应的值总是会被初始化。
- 使用结构字面量声明一个结构类型的变量
// 声明 user 类型的变量,并初始化所有字段
lisa := user{
name: "Lisa",
email: "lisa@email.com",
ext: 123,
privileged: true,
}
- 使用结构字面量创建结构类型的值
第一种在不同行,使用字段名
user{
name:"Lisa",
email: "lisa@email.com",
ext: 123,
privileged: true,
}
第二种,再同一行,不适用字段名,但必须要和结构生命中字段的顺序一致。结尾不需要逗号。
lisa := user{"Lisa", "lisa@email.com", 123, true}
- 基于一个已有的类型,将其作为新类型的类型说明。(当需要一个可以用已有类型标识的新类型的时候)。
type Duration int64
var dur Duration
dur = int64(1000)
// 报错,int64和Duration类型不同不能相互赋值
方法
方法能给用户定义的类型添加新的行为,实际上也是函数,只是在生命时,在关键字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()
}
关键字func和函数名之间的参数被称作接收者,将函数和接收者的类型绑在一起。如果一个函数又接收者,这个函数就被称为方法。
接受者又两种类型:1)值接收者 2)指针接收者。值接收者使用值的副本调用方法,指针接收者使用实际值来调用方法。
值接收者可以接受指针,Go编译器为了支持此操作,编译时指针被解引用为值(*lisa).notify()
。也可以使用一个值调用使用指针接收者声明的方法。Go编译器背后做的事情。(&bill).changeEmail("bill@newdomain.com")
类型的本质
在声明一个新类型后,声明一个该类型的方法之前,需要弄清楚这个类型的本质:时按值做传递还是安置真做传递。
- 内置类型:这些类型本质上时原始的类型,按值传递。
- 应用类型:引用类型包括 1)切片 2)映射 3)通道 4)接口 5)函数。和原始数据同等对待。
- 结构类型:既可以时原始的,也可以是非原始的。
到底是使用值接收者还是指针接收者,不应该由该方法是否修改了接收到的值决定。这个决定应该基于该类型的本质。
接口
- 接口的实现
接口值是一个两个字长度的数据结构,第一个子包含一个指向内部表的指针,这个内部表叫做iTable,包含了所存储的值的类型信息以及于这个值相关联的一组方法;第二个字是一个指向所存储值的指针。将类型信息和指针组合在一起,就将这两个值组成了一种特殊的关系。- 值赋值后接口变量的值的内部布局
- 指针变量
- 方法集
方法集定义了接口的接受规则。以下是规范里描述的方法集:
Values | Methods Receivers |
---|---|
T | (t T) |
*T | (t T) and (t *T) |
Methods Receivers | Values |
---|---|
(t T) | T and *T |
(t *T) | *T |
规则说明:如果使用指针接收者来实现一个接口,那么只有指向那个类型的指针才能够实现对应的接口。如果使用值接收者来实现一个接口,那么那个类型的值和指针都能够实现对应的接口。
为什么会出现这种限制?事实上,编译器并不是总能自动获得一个值的地址。
- 多态
// 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()
}
- 嵌入类型
- 与内部类型相关的标识符会提升到外部类型上。这些被提升的标识符就像直接生命在外部类型里的标识符一样,也是外部类型的一部分。
- 外部类型可以通过声明与内部类型标识符同名的标识符来覆盖内部标识符的字段或方法。
由于内部类型的提升,内部类型实现的接口会自动提升到外部类型。这意味着由于内部类型的实现,外部类型也同样实现了这个接口。
// Sample program to show how embedded types work with interfaces.
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 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
}
// 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",
},
level: "super",
}
// Send the admin user a notification.
// The embedded inner type's implementation of the
// interface is "promoted" to the outer type.
sendNotification(&ad)
}
// sendNotification accepts values that implement the notifier
// interface and sends notifications.
func sendNotification(n notifier) {
n.notify()
}
// output
Sending user email to john smith<john@yahoo.com>
如果外部类型实现了notify方法,内部类型的实现就不会被提升。
// 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()
}
// user defines a user in the program.
type user struct {
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
}
// 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",
},
level: "super",
}
// 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()
}
// sendNotification accepts values that implement the notifier
// interface and sends notifications.
func sendNotification(n notifier) {
n.notify()
}
// output
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>
公开或未公开的标识符
当一个标识符的名字以小写字母开头是,这个标识符就是未公开的,即包外的代码不可见。如果一个标识符以大写字母开头,这个标识符就是公开的,即被包外的代码可见。
将工厂函数命名为 New 是 Go 语言的一个习惯。
// counters 包提供告警计数器的功能
package counters
// alertCounter 是一个为公开的类型
// 这个类型用于保存告警计数
type alertCounter int
// New 创建并返回一个未公开的 alertCounter 类型的值
func New(value int) alertCounter {
return alertCounter(value)
}
// main函数片段
func main() {
// Create a variable of the unexported type using the exported
// New function from the package counters.
counter := counters.New(10)
fmt.Printf("Counter: %d\n", counter)
}
为什么main函数能够接受通过New返回的alertCounter变量的值。理由:
- 公开或者未公开的标识符不是一个值
- 短变量声明操作符,有能力捕捉引用的类型,并创建一个为公开的类型的变量。
结构里字段的权限
type User struct {
Name string
email string
}
// main is the entry point for the application.
func main() {
// Create a value of type User from the entities package.
u := entities.User{
Name: "Bill",
email: "bill@email.com", // 未知
}
// ./example71.go:16: unknown entities.User field 'email' in
// struct literal
fmt.Printf("User: %v\n", u)
}
公开和未公开的内嵌类型是如何工作的
// Package entities contains support for types of
// people in the system.
package entities
// user defines a user in the program.
type user struct {
Name string
Email string
}
// Admin defines an admin in the program.
type Admin struct {
user // The embedded type is unexported.
Rights int
}
// Sample program to show how unexported fields from an exported
// struct type can't be accessed directly.
package main
import (
"fmt"
"./entities"
)
// main is the entry point for the application.
func main() {
// Create a value of type Admin from the entities package.
a := entities.Admin{
Rights: 10,
}
// Set the exported fields from the unexported
// inner type.
// 类型提升后外部类型直接访问
a.Name = "Bill"
a.Email = "bill@email.com"
fmt.Printf("User: %v\n", a)
}