文档章节

Go结构体——嵌入结构体

秋风醉了
 秋风醉了
发布于 2016/07/10 13:12
字数 1461
阅读 294
收藏 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=========

© 著作权归作者所有

共有 人打赏支持
秋风醉了
粉丝 236
博文 578
码字总数 419908
作品 0
朝阳
程序员
【go语言】读书随笔

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

qingkechina
2017/02/15
0
0
『Go 语言学习专栏』-- 第八期

大家好,我是谢伟,是一名程序员。 下面的学习是一个系列,力求从初学者的角度学会go 语言,达到中级程序员水平。 这一系列是我的输出总结,同时我还推出了视频版。正在制作过程。 为写出这些...

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

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

DeepTech深科技
06/18
0
0
字符设备驱动基础篇3——字符设备驱动工作原理

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

oqqHuTu12345678
2017/07/19
0
0
Linux设备驱动模型2——总线式设备驱动组织方式

以下内容源于朱有鹏嵌入式课程的学习,如有侵权,请告知删除。 更深入理解资料:http://blog.csdn.net/oqqhutu12345678/article/details/78933386 1、总线 (1)物理上的真实总线及其作用(英...

oqqHuTu12345678
2017/07/21
0
0

没有更多内容

加载失败,请刷新页面

加载更多

javascript source map 的使用

之前发现VS.NET会为压缩的js文添加一个与文件名同名的.map文件,一直没有搞懂他是用来做什么的,直接删除掉运行时浏览器又会报错,后来google了一直才真正搞懂了这个小小的map文件背后的巨大...

粒子数反转
昨天
1
0
谈谈如何学Linux和它在如今社会的影响

昨天,还在农耕脑力社会,今天已经人工智能技术、大数据、信息技术的科技社会了,高速开展并迅速浸透到当今科技社会的各个方面,Linux日益成为人们信息时代的到来,更加考验我们对信息的处理程...

linux-tao
昨天
0
0
学习设计模式——中介者模式

1. 认识中介者模式 1. 定义:用一个中介对象来封装一系列的对象交互行为,中介者使得各对象不需要显式的互相引用,从而使其松散耦合,独立的改变他们之间的交互。 2. 结构: Mediator:中介者...

江左煤郎
昨天
1
0
深入理解Plasma(1):Plasma 框架

这一系列文章将围绕以太坊的二层扩容框架,介绍其基本运行原理,具体操作细节,安全性讨论以及未来研究方向等。本篇文章作为开篇,主要目的是理解 Plasma 框架。 Plasma 作为以太坊的二层扩容...

HiBlock
昨天
0
0
Java 8 日期 示例

Java 8 推出了全新的日期时间API。Java处理日期、日历和时间的方式一直为社区所诟病,将 java.util.Date设定为可变类型,以及SimpleDateFormat的非线程安全使其应用非常受限。Java也意识到需...

阿刚ABC
昨天
2
0

没有更多内容

加载失败,请刷新页面

加载更多

返回顶部
顶部