文档章节

Go结构体——嵌入结构体

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

© 著作权归作者所有

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

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

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

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

DeepTech深科技
06/18
0
0
【ELIXIR】简单说下elixir的历史

2011年 Jose Valim 在github上发布了elixir的第一个commit 在2011年之前,他收到了一些启发: 2005年Herb Sutter的文章——免费午餐结束了 2007年Joe Armstrong的书——Erlang编程 2009年Rai...

ljzn
2016/09/17
197
0
Swift讲解专题十——类与结构体

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

珲少
2016/05/16
98
0
C语言/C++编程基础入门学习—循环结构

C语言是面向过程的,而C++是面向对象的 C和C++的区别: C是一个结构化语言,它的重点在于算法和数据结构。C程序的设计首要考虑的是如何通过一个过程,对输入(或环境条件)进行运算处理得到...

小辰带你看世界
03/31
0
0
QtInternal 之 Resources

注意:本文翻译自 http://developer.qt.nokia.com 中的 Resources ,中文译文见 简体中文版 ,如果你对翻译wiki感兴趣,请参考Wiki中文帮助 概述 资源是将包括图像、声音等任意二进制数据作为...

晨曦之光
2012/05/08
126
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
迅为4412开发板Linux驱动教程——总线_设备_驱动注册流程详解

视频下载地址: 驱动注册:http://pan.baidu.com/s/1i34HcDB 设备注册:http://pan.baidu.com/s/1kTlGkcR 总线设备驱动注册流程详解 • 注册流程图 • 设备一般都需要先注册,才能注册驱动 ...

topeet
2015/08/12
128
0
Go使用组合实现继承

Go使用组合实现继承 Go是面向对象的,但没有如下概念: class类 extends继承 implements实现 Go的面向对象不支持继承。可能够使用Go中一种叫做"组合"(Composition)的方法来实现继承。 Go组...

秋风醉了
2016/10/09
44
0

没有更多内容

加载失败,请刷新页面

加载更多

下一页

StringUtils类中isEmpty与isBlank的区别

org.apache.commons.lang.StringUtils类提供了String的常用操作,最为常用的判空有如下两种isEmpty(String str)和isBlank(String str)。 StringUtils.isEmpty(String str) 判断某字符串是否为...

说回答
11分钟前
0
0
react native使用redux快速上手

先看个简单demo //app.jsimport React, {Component} from 'react';import {StyleSheet, Button, View} from 'react-native';import TestView from './src/testView'export default......

燕归南
12分钟前
0
0
页面输出JSON格式数据

package com.sysware.utils;import java.io.IOException;import javax.servlet.ServletResponse;import org.apache.log4j.Logger;import com.sysware.SyswareConstant;pub......

AK灬
34分钟前
0
0
springCloud-2.搭建Eureka Client的使用

1.使用IDEA,Spring Initializr创建 2.填写项目资料 3.选择spring boot版本,插件选择Cloud Discovery→Eureka Discovery 4.选择保存地址 5.修改application.yml eureka: client: s...

贺小康
37分钟前
0
0
CenOS 6.5 RPM 安装 elasticsearch 6.3.1

下载 wget --no-check-certificate https://artifacts.elastic.co/downloads/elasticsearch/elasticsearch-6.3.1.rpm...

阿白
39分钟前
0
0
1.4 创建虚拟机&1.5 安装CentOS7&1.6 配置ip(上)&1.7 配置ip(下)

1.4 创建虚拟机 知识点 虚拟机网络链接模式 桥连 直接将虚拟网卡桥接到一个物理网卡上面。需要手工为虚拟系统配置IP地址、子网掩码,而且还要和宿主机器处于同一网段,这样虚拟系统才能和宿主...

小丑鱼00
46分钟前
0
0
TrustAsia(亚洲诚信)助力看雪2018安全开发者峰会

2018年7月21日,看雪2018安全开发者峰会在北京国家会议中心圆满落下帷幕。拥有18年悠久历史的老牌安全技术社区——看雪学院联手国内最大开发者社区CSDN,汇聚业内顶尖的安全开发者和技术专家...

亚洲诚信
47分钟前
0
0
Spring注解介绍

@Resource、@AutoWired、@Qualifier 都用来注入对象。其中@Resource可以以 name 或 type 方式注入,@AutoWired只能以 type 方式注入,@Qualifier 只能以 name 方式注入。 但它们有一些细微区...

lqlm
57分钟前
0
0
32位汇编在64位Ubuntu上的汇编和连接

本教程使用的操作系统是Ubuntu Linux 18.04 LTS版本,汇编器是GNU AS(简称as),连接器是GNU LD(简称ld)。 以下是一段用于检测CPU品牌的汇编小程序(cpuid2.s): .section .dataoutput...

ryanliue
今天
0
0
CentOS系统启动报错Failed to mount /sysroot解决方法

xfs_repair -v -L /dev/dm-0

Mr_Tea伯奕
今天
1
0

没有更多内容

加载失败,请刷新页面

加载更多

下一页

返回顶部
顶部