go语言嵌套类型的使用细节

1. 定义

  • 在Go语言中,嵌套类型是将已有的类型直接声明在新的结构类型里。被嵌入的类型被称为新的外部类型的内部类型。
  • 通过嵌入类型,与内部类型相关的成员变量会提升到外部类型上。就好像这些成员变量直接声明在外部类型一样。如下图所示:

  • 外部类型可以通过声明与内部类型成员变量同名的成员变量来覆盖内部类型的成员变量。如下图所示:

2. 嵌套类型的基本使用

package main

    import "fmt"

    // 自定义类型
    type userInfo struct {
        name string
        email string
    }

    // 自定义类型
    type client struct {
        userInfo // 嵌入类型
        level string
    }

    // 定义user指针类型调用的方法
    func (u *userInfo) notify() {
        // 模拟发邮件的功能
        fmt.Printf("Send email to %s\n", u.name, u.email)
    }

    func main() {
        // 创建一个client
        c := client{
            userInfo:userInfo{
                name:"lioney",
                email:"lioney_liu@sina.com",
            },
            level: "normal",
        }

        // 直接访问内部类型的方法
        c.userInfo.notify()  // Send email to lioney

        // 内部类型的方法也被提升到外部类型
        c.notify()  // Send email to lioney
    }
  • 在以上代码中,内部类型userInfo的初始化使用结构字面量完成的。通过内部类型的名字userInfo可以访问内部类型的字段或方法。
  • 对外部类型client来说,内部类型总是存在的。借助内部类型提升,notify方法可以直接通过外部类型的变量c来访问, 实际上所谓的内部类型的提升只是Go编译器帮我们完成了一次间接查找
  • Go编译器内部对指针进行了优化,c.user.notify()和c.notify()实际上内部执行都是&(c.user).notify(), 因为内部类型的方法是通过指针接收者定义的。

3. 将嵌套类型应用于接口

package main

    import "fmt"

    // 定义一个通知行为的接口
    type notifier interface {
        notify()
    }

    // 定义一个接收接口类型参数的函数
    func sendNotification(n notifier) {
        n.notify()
    }

    // 自定义类型
    type userInfo struct {
        name string
        email string
    }

    // 自定义类型
    type client struct {
        userInfo // 嵌入类型
        level string
    }

    // 定义user指针类型调用的方法
    func (u *userInfo) notify() {
        // 模拟发邮件的功能
        fmt.Printf("Send email to %s\n", u.name, u.email)
    }


    func main() {
        // 创建一个client
        c := client{
            userInfo:userInfo{
                name:"lioney",
                email:"lioney_liu@sina.com",
            },
            level: "normal",
        }
        // 实现接口的内部类型的方法,被提升到了外部类型
        sendNotification(&c)  // Send email to lioney
        
        // 多态调用必须遵守方法集规则
        //sendNotification(c)  // client does not implement notifier (notify method has pointer receiver)
    }
  • 在上面代码中,由于内部类型的提升,内部类型实现的接口会自动提升到外部类型,可以当成外部类型实现了该接口,我们由此可以实现对外部类型的多态调用。
  • 有一个重要的问题必须指出: 在进行多态调用的时候,必须满足方法集的规则, ,方法集定义了接口的参数接收规则,方法集的规则如下表所示:

    方法接收者的类型 参数的类型
    值或指针
    指针 指针
  • 由于是内部类型的指针接收者实现了notify方法,所以在多态调用的时候,必须传入外部类型的地址才能和接口调用相匹配。

4. 内部类型和外部类型实现同一个方法

package main

import "fmt"

// 自定义类型
type userInfo struct {
    name string
    email string
}

// 自定义类型
type client struct {
    userInfo // 嵌入类型
    level string
}

// 定义userInfo指针类型调用的方法
func (u *userInfo) notify() {
    // 模拟发邮件的功能
    fmt.Printf("userInfo notify(), Send email to %s.\n", u.name)
}

// 定义client指针类型调用的方法
func (c *client) notify() {
    fmt.Printf("client notify(), Send email to %s.\n", c.name)
}

func main() {
    // 创建一个client
    c := client{
        userInfo:userInfo{
            name:"lioney",
            email:"lioney_liu@sina.com",
        },
        level: "normal",
    }

    // 直接调用
    c.notify()  // client notify(), Send email to lioney.

    // 通过内部类型的变量名调用
    c.userInfo.notify()  // userInfo notify(), Send email to lioney.
}

从以上代码可以看出, 如果外部类型和内部类型都实现了同一方法notify(),则内部类型的notify()方法不会被提升,但一直存在,可以通过内部类型的变量名来间接调用

嵌套类型的内容是不是很简单呢*_*

我是lioney,年轻的后端攻城狮一枚,爱钻研,爱技术,爱分享。 个人笔记,整理不易,感谢阅读、点赞和收藏。 文章有任何问题欢迎大家指出,也欢迎大家一起交流后端各种问题!