文档章节

Go结构体——嵌入结构体

秋风醉了
 秋风醉了
发布于 2016/07/10 13:12
字数 1461
阅读 376
收藏 0

Go结构体——嵌入结构体

结构体类型可以包含匿名或者嵌入字段。也叫做嵌入一个类型。

当我们嵌入一个类型到结构体中时,该类型的名字充当了嵌入字段的字段名。

来看看ColoredPoint这个类型:

type Point struct{ X, Y float64 }

type ColoredPoint struct { 
        //结构体可以包含一个或多个 匿名(或内嵌)字段,即这些字段没有显式的名字,只有字段的类型是必须的,此时类型也就是字段的名字。
        //匿名字段本身可以是一个结构体类型,即 结构体可以包含内嵌结构体。
	Point 
	Color color.RGBA
}

我们完全可以将ColoredPoint定义为一个有三个字段的struct,但是我们却将Point这个类型嵌入到ColoredPoint来提供X和Y这两个字段。内嵌可以使我们在定义ColoredPoint时得到一种句法上的简写形式,并使其包含Point类型所具有的一切字段,然后再定义一些自己的。如果我们想要的话,我们可以直接认为通过嵌入的字段就是ColoredPoint自身的字段,而完全不需要在调用时指出Point,比如下面这样,

package main

import (
	"fmt"
	"image/color"
)

func main() {
	var cp ColoredPoint
	cp.X = 1
	fmt.Println(cp.Point.X) // "1"
	cp.Point.Y = 2
	fmt.Println(cp.Y) // "2"

}

type Point struct{ X, Y float64 }

type ColoredPoint struct {
        //结构体可以包含一个或多个 匿名(或内嵌)字段,即这些字段没有显式的名字,只有字段的类型是必须的,此时类型也就是字段的名字。
        //匿名字段本身可以是一个结构体类型,即 结构体可以包含内嵌结构体。
	Point
	Color color.RGBA
}

对于Point中的方法我们也有类似的用法,我们可以把ColoredPoint类型当作接收器来调用Point里的方法,

即使ColoredPoint里没有声明这些方法:

package main

import (
	"fmt"
	"image/color"
	"math"
)

func main() {
	var cp ColoredPoint
	cp.X = 1
	fmt.Println(cp.Point.X) // "1"
	cp.Point.Y = 2
	fmt.Println(cp.Y) // "2"

	red := color.RGBA{255, 0, 0, 255}
	blue := color.RGBA{0, 0, 255, 255}
	var p = ColoredPoint{Point{1, 1}, red}
	var q = ColoredPoint{Point{5, 4}, blue}
	fmt.Println(p.Distance(q.Point)) // "5"
	p.ScaleBy(2)
	q.ScaleBy(2)
	fmt.Println(p.Distance(q.Point)) // "10"

}

type Point struct{ X, Y float64 }

type ColoredPoint struct {
        //结构体可以包含一个或多个 匿名(或内嵌)字段,即这些字段没有显式的名字,只有字段的类型是必须的,此时类型也就是字段的名字。
        //匿名字段本身可以是一个结构体类型,即 结构体可以包含内嵌结构体。
	Point 
	Color color.RGBA
}


// traditional function
func Distance(p, q Point) float64 {
	return math.Hypot(q.X-p.X, q.Y-p.Y)
}

// same thing, but as a method of the Point type
func (p Point) Distance(q Point) float64 {
	return math.Hypot(q.X-p.X, q.Y-p.Y)
}

// 指针对象的方法,需要类型 Point 的指针调用该方法
// 这个方法的名字是(*Point).ScaleBy。这里的括号是必须的;
// 没有括号的话这个表达式可能会被理解为*(Point.ScaleBy)。
func (p *Point) ScaleBy(factor float64) {
	p.X *= factor
	p.Y *= factor
}

Point类的方法也被引入了ColoredPoint。用这种方式,内嵌可以使我们定义字段特别多的复杂类型,我 们可以将字段先按小类型分组,然后定义小类型的方法,之后再把它们组合起来。

读者如果对基于类来实现面向对象的语言比较熟悉的话,可能会倾向于将Point看作一个基类,而ColoredPoint看作其子类或者继承类,或者将ColoredPoint看作"is a" Point类型。但这是错误的理解。请注意上面例子中对Distance方法的调用。Distance有一个参数是Point类型,但q并不是一个Point类,所以尽管q有着Point这个内嵌类型,我们也必须要显式地选择它。

一个ColoredPoint并不是一个Point,但他"has a"Point,并且它有从Point类里引入的Distance和ScaleBy方法。如果你喜欢从实现的角度来考虑问题,内嵌字段会指导编译器去生成额外的包装方法来委托已经声明好的方法,和下面的形式是等价的:

func (p ColoredPoint) Distance(q Point) float64 {
	return p.Point.Distance(q)
}

func (p *ColoredPoint) ScaleBy(factor float64) {
	p.Point.ScaleBy(factor)
}

在类型中内嵌的匿名字段也可能是一个指针。下面这个ColoredPoint的声明内嵌了一个*Point的指针。

package main

import (
	"fmt"
	"image/color"
	"math"
)

func main() {

	red := color.RGBA{255, 0, 0, 255}
	blue := color.RGBA{0, 0, 255, 255}

	p := ColoredPoint{&Point{1, 1}, red}
	q := ColoredPoint{&Point{5, 4}, blue}

	// *q.Point
	fmt.Println(p.Distance(*q.Point)) // "5"
	q.Point = p.Point                 // p and q now share the same Point
	p.ScaleBy(2)
	fmt.Println(*p.Point, *q.Point) // "{2 2} {2 2}"
}

type Point struct{ X, Y float64 }

type ColoredPoint struct {
	*Point
	Color color.RGBA
}

// traditional function
func Distance(p, q Point) float64 {
	return math.Hypot(q.X-p.X, q.Y-p.Y)
}

// same thing, but as a method of the Point type
func (p Point) Distance(q Point) float64 {
	return math.Hypot(q.X-p.X, q.Y-p.Y)
}

// 指针对象的方法,需要类型 Point 的指针调用该方法
// 这个方法的名字是(*Point).ScaleBy。这里的括号是必须的;没有括号的话这个表达式可能会被理解为*(Point.ScaleBy)。
func (p *Point) ScaleBy(factor float64) {
	p.X *= factor
	p.Y *= factor
}

func (p ColoredPoint) Distance(q Point) float64 {
	return p.Point.Distance(q)
}

func (p *ColoredPoint) ScaleBy(factor float64) {
	p.Point.ScaleBy(factor)
}

一个struct类型也可能会有多个匿名字段。我们将ColoredPoint定义为下面这样:

type ColoredPoint struct {
    Point
    color.RGBA
}

然后这种类型的值便会拥有Point和RGBA类型的所有方法,以及直接定义在ColoredPoint中的方法。当编译器解析一个选择器到方法时,比如p.ScaleBy,它会首先去找直接定义在这个类型里的ScaleBy方法,然后找被ColoredPoint的内嵌字段们引入的方法,然后去找Point和RGBA的内嵌字段引入的方法,然后一直递归向下找。如果选择器有二义性的话编译器会报错,比如你在同一级里有两个同名的方法。

=========END=========

© 著作权归作者所有

共有 人打赏支持
秋风醉了
粉丝 241
博文 566
码字总数 417505
作品 0
朝阳
程序员
私信 提问
【go语言】读书随笔

在结构体中如果一个字段声明只有类型而没有指定名称,则这个字段叫做匿名字段。匿名字段的类型必须由一个数据类型的名称(比如int、string、Sortable等)或者一个非接口类型对应的指针类型的...

qingkechina
2017/02/15
0
0
这个 3D 打印的磁性结构体能够爬行、滚动、跳跃,甚至还能玩接球

  美国麻省理工学院的工程师们创造了一种 3D 打印的软性结构体,其运动能通过外部磁场控制,就好似没有绳子的木偶一般。   这一结构体能够在磁铁的控制下进行多种变形:一个在磁力作用下...

DeepTech深科技
2018/06/18
0
0
block本质探寻一之内存结构

一、代码——命令行模式 //main.m #import struct block_impl { }; struct mainblockdesc_0 { }; struct mainblockimpl_0 { }; int main(int argc, const char * argv[]) { // ^{// NSLog(@"......

春天里的花骨朵
01/09
0
0
字符设备驱动基础篇3——字符设备驱动工作原理

以下内容源于朱有鹏嵌入式课程的学习,如有侵权,请告知删除。 参考资料:http://www.cnblogs.com/biaohc/p/6575074.html。 1、系统整体工作原理 (1)应用层-》API-》设备驱动-》硬件; (2...

oqqHuTu12345678
2017/07/19
0
0
Swift讲解专题十——类与结构体

Swift讲解专题十——类与结构体 一、引言 Swift中的类与结构体十分相似,和Objective-C不同的是,Swift中的结构体不仅可以定义属性,也可以像类一样为其定义方法。 Swift中的类与结构体有如下...

珲少
2016/05/16
98
0

没有更多内容

加载失败,请刷新页面

加载更多

大数据教程(11.9)hive操作基础知识

上一篇博客分享了hive的简介和初体验,本节博主将继续分享一些hive的操作的基础知识。 DDL操作 (1)创建表 #建表语法CREATE [EXTERNAL] TABLE [IF NOT EXISTS] table_name [(col_name ...

em_aaron
今天
2
0
OSChina 周四乱弹 —— 我家猫真会后空翻

Osc乱弹歌单(2019)请戳(这里) 【今日歌曲】 @我没有抓狂 :#今天听这个# 我艇牛逼,百听不厌,太好听辣 分享 Led Zeppelin 的歌曲《Stairway To Heaven》 《Stairway To Heaven》- Led Z...

小小编辑
今天
4
0
node调用dll

先安装python2.7 安装node-gyp cnpm install node-gyp -g 新建一个Electron-vue项目(案例用Electron-vue) vue init simulatedgreg/electron-vue my-project 安装electron-rebuild cnpm ins......

Chason-洪
今天
3
0
scala学习(一)

学习Spark之前需要学习Scala。 参考学习的书籍:快学Scala

柠檬果过
今天
3
0
通俗易懂解释网络工程中的技术,如STP,HSRP等

导读 在面试时,比如被问到HSRP的主备切换时间时多久,STP几个状态的停留时间,自己知道有这些东西,但在工作中不会经常用到,就老是记不住,觉得可能还是自己基础不够牢固,知识掌握不够全面...

问题终结者
昨天
4
0

没有更多内容

加载失败,请刷新页面

加载更多

返回顶部
顶部