Go语言类型系统

2019-12-09  本文已影响0人  LightiSnow

用户定义的类型

// 声明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")

类型的本质

在声明一个新类型后,声明一个该类型的方法之前,需要弄清楚这个类型的本质:时按值做传递还是安置真做传递。

到底是使用值接收者还是指针接收者,不应该由该方法是否修改了接收到的值决定。这个决定应该基于该类型的本质。

接口

接口变量布局 指针变量布局

方法集定义了接口的接受规则。以下是规范里描述的方法集:

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)
}
上一篇下一篇

猜你喜欢

热点阅读