golang embedded structs
golang 中把struct 转成json格式输出
package main import (
"encoding/json"
"fmt"
) type Person struct {
Name string `json:"name,omitempty"`
DoB string `json:"dob,omitempty"`
Age string `json:"-,omitempty"`
} type Singer struct {
Person
MusicGenre string `json:"music_genre,omitempty"`
Age string `json:"ageee,omitempty"`
} func ToJSON(s interface{}) (string, error) {
bs, err := json.Marshal(s)
if err != nil {
return "", err
}
// fmt.Println(bs)
return string(bs), nil
} func main() {
s := Singer{
Person{"John Singer",
"01-02-1975",
"13"},
"pop", "12" }
sJSON, _ := ToJSON(s)
fmt.Println(sJSON)
// {"name":"John Singer","dob":"01-02-1975","music_genre":"pop"}
}
---------------------------------
One of the things that I missed the most, apart from generics, when coming to Go while having Java background was the lack of inheritance. In JVM it was pretty simple, as you can define a parent and child classes, then have a common behavior present all the children objects. This is not the case in Go, and I had to learn how to work around this. Here, we don't have inheritance, we have the composition, and for that, we have a pretty useful technique called embedding structs.
Manual composition vs embedding
In the classical approach to composition, we add what we'd call a parent class as another field in the child one. In Go terms, we should first define a parent struct with common attributes:
type Person struct {
Name string
}
Then we should add an attribute of type Person
to another struct which we want to use that common features from the parent:
type Singer struct {
Parent Person
MusicGenre string
}
This may not seem like a huge improvement, but we can use a shorter notation and embedPerson
into Singer
by skipping the attribute name and just leave its type:
type Singer struct {
Person
MusicGenre string
}
In both cases, when creating an instance of Singer
inline we need to specify a definition of parent struct with its name - when embedding it is the name of that struct:
// manual composition
s0 := embedding.Singer{
Parent: embedding.Person{Name: "John Singer"},
MusicGenre: "pop",
}
// embedding
s1 := embedding.Singer{
Person: embedding.Person{Name: "John Singer"},
MusicGenre: "pop",
}
The difference is larger than just saving a few characters in the struct definition. First of all, when doing it manually we'd have to use attribute name (Parent
) all the time:
// manual composition
fmt.Println(s0.Parent.Name)
While with embedding we can (but don't have to) refer to the attributes of the embedded struct as if they were defined in the child:
// embedding
fmt.Println(s1.Parent.Name)
fmt.Println(s1.Name)
Another interesting thing is that if we want to build a Singer
struct by adding attributes to an empty instance, again we get a feeling of the inheritance:
// manual composition
s0 := embedding.Singer{MusicGenre: "pop"}
s0.Parent.Name = "John Singer" // we have to do this explicitly
fmt.Println(s0.Parent.Name)
// embedding
s1 := embedding.Singer{MusicGenre: "pop"}
// we can be explicit with
// s1.Person.Name = "John Doe"
// but we can as well do this:
s1.Name = "John Singer"
fmt.Println(s1.Parent.Name)
Calling inherited functions
Another useful feature of embedding structs is that we can call parent's functions that were inherited as if they were defined on a child struct. For example, we can add a Talk(..)
function on Person
and use it on an instance of Singer
:
type Person struct {
...
}
func (p Person) Talk(message string) {
fmt.Printf("%s (a person) says \"%s\"\n", p.Name, message)
}
func main() {
s := Singer{}
s.Name = "John Doe"
s.Talk("Hello, reader!")
// John Doe (a person) says "Hello, reader!"
}
Awesome! Remember that the attributes and function are promoted to the child struct only if they are not overwritten there. If we define a Talk(..)
function directly on Singer
, the one from Person
would never be called:
...
func (p Singer) Talk(message string) {
fmt.Printf("Singer %s says \"%s\"\n", p.Name, message)
}
func main() {
s := Singer{}
s.Name = "John Doe"
s.Talk("Hello, again!")
// Singer John Singer says "Hello again!"
}
The trick is when a function that is promoted calls another one. For example, if we define a Type()
function that would return the struct identifier on both Person
and Singer
, then call it within Talk
function that would be promoted from the parent, we would get the Type()
from the parent as well. The reason for this is that at the time of executing the function, Go does not realize we are dealing with some inheritance stuff and we don't go back to the original caller to see what the context is:
func (p Person) Type() string {
return "PERSON"
}
func (p Person) Talk(message string) {
fmt.Printf("%s (type=%s) says \"%s\"\n", p.Name, p.Type(), message)
}
...
func (s Singer) Type() string {
return "SINGER"
}
func (s Singer) Sing(title string) {
fmt.Printf("%s (type=%s) sings %s in the style of %s.\n", s.Name, s.Type(), title, s.MusicGenre)
}
...
func main() {
s := Singer{MusicGenre: "rock"}
s.Name = "Johny Singerra"
s.Sing("Welcome to the forest")
// Johny Singerra (type=SINGER) sings Welcome to the forest in the style of rock.
s.Talk("Hello!")
// Johny Singerra (type=PERSON) says "Hello!"
}
Hiding JSON properties
Another interesting fact is the way Go handler JSON tags that can identify attributes of a struct. For example, we can marshal Singer
to JSON and see that both its own and the inherited properties end up in the document:
type Person struct {
Name string `json:"name,omitempty"`
DoB string `json:"dob,omitempty"`
}
...
type Singer struct {
Person
MusicGenre string `json:"music_genre,omitempty"`
}
...
func (s Singer) ToJSON() (string, error) {
bs, err := json.Marshal(s)
if err != nil {
return "", err
}
return string(bs), nil
}
...
func main() {
s := embedding.Singer{
Person: embedding.Person{
Name: "John Singer",
DoB: "01-02-1975",
},
MusicGenre: "pop",
}
sJSON, _ := s.ToJSON()
fmt.Println(sJSON)
// {"name":"John Singer","dob":"01-02-1975","music_genre":"pop"}
}
It's great that both name
and dob
properties are there, but what if we want to hide some of those for JSON marshaler? Let's create a MusicStar
struct where we'll add a nickname to the Singer
, but at the same time we'd like to hide the date of birth (since we want to keep it as a secret):
type MusicStar struct {
Singer
Nickname string `json:"nickname,omitempty"`
DoB string `json:"-,omitempty"`
}
func (ms MusicStar) ToJSON() (string, error) {
bs, err := json.Marshal(ms)
if err != nil {
return "", err
}
return string(bs), nil
}
Note that we've added a DoB field but added a -
as JSON tag to indicate that this field should be removed from marshaling. Unfortunately, that doesn't work:
func main() {
ms := embedding.MusicStar{
Nickname: "Starry",
Singer: embedding.Singer{
Person: embedding.Person{
Name: "Joe Star",
DoB: "01-02-1975",
},
MusicGenre: "pop",
},
}
msJSON, _ := ms.ToJSON()
fmt.Println(msJSON)
// "name":"Joe Star","dob":"01-02-1975","music_genre":"pop","nickname":"Starry"}
}
That is because although Go sees our -
JSON tag, it recognizes it as undefined and go deeper into embedded structs to see if there is a field that matches the name, but has some concrete tag defined. It finds one, so that is being used. We can, however, trick the language into hiding that nested DoB
, by defining the same property with the same JSON tag in the top-level struct. This way the DoB
from Person
will never be promoted to the top level JSON object, since its top-level value is empty, therefore the empty string overwrites anything that comes from Person
:
type MusicStar struct {
Singer
Nickname string `json:"nickname,omitempty"`
DoB string `json:"dob,omitempty"`
}
...
msJSON, _ := ms.ToJSON()
fmt.Println(msJSON)
// {"name":"Joe Star","music_genre":"pop","nickname":"Starry"}
As you can see, embedded structs solve some of the things we would achieve via classical inheritance, but it's necessary to understand how it works to avoid unexpected behaviors and gotchas. The full source code of these examples is available on Github.
golang embedded structs的更多相关文章
- 从OOP的角度看Golang
资料来源 https://github.com/luciotato/golang-notes/blob/master/OOP.md?hmsr=toutiao.io&utm_medium=tou ...
- golang Methods on structs
原文:http://golangtutorials.blogspot.com/2011/06/methods-on-structs.html snmp 下载,有空学习一下! https://sourc ...
- Go语言(golang)开源项目大全
转http://www.open-open.com/lib/view/open1396063913278.html内容目录Astronomy构建工具缓存云计算命令行选项解析器命令行工具压缩配置文件解析 ...
- [转]Go语言(golang)开源项目大全
内容目录 Astronomy 构建工具 缓存 云计算 命令行选项解析器 命令行工具 压缩 配置文件解析器 控制台用户界面 加密 数据处理 数据结构 数据库和存储 开发工具 分布式/网格计算 文档 编辑 ...
- Golang优秀开源项目汇总, 10大流行Go语言开源项目, golang 开源项目全集(golang/go/wiki/Projects), GitHub上优秀的Go开源项目
Golang优秀开源项目汇总(持续更新...)我把这个汇总放在github上了, 后面更新也会在github上更新. https://github.com/hackstoic/golang-open- ...
- golang sync.noCopy 类型 —— 初探 copylocks 与 empty struct
问题引入 学习golang(v1.16)的 WaitGroup 代码时,看到了一处奇怪的用法,见下方类型定义: type WaitGroup struct { noCopy noCopy ... } ...
- Golang通脉之面向对象
面向对象的三大特征: 封装:隐藏对象的属性和实现细节,仅对外提供公共访问方式 继承:使得子类具有父类的属性和方法或者重新定义.追加属性和方法等 多态:不同对象中同种行为的不同实现方式 Go并不是一个纯 ...
- 【转载】关于Embedded Linux启动的经典问题
转载自:http://linux.chinaunix.net/techdoc/install/2009/04/13/1107608.shtml 发信人: armlinux (armlinux), 信区 ...
- [转]50 Shades of Go: Traps, Gotchas, and Common Mistakes for New Golang Devs
http://devs.cloudimmunity.com/gotchas-and-common-mistakes-in-go-golang/ 50 Shades of Go: Traps, Gotc ...
随机推荐
- [Cometoj#3 C]子序列子序列子序列..._动态规划_数论
子序列子序列子序列... 题目链接:https://cometoj.com/contest/38/problem/C?problem_id=1542 数据范围:略. 题解: 神仙题,感觉这个题比$D$ ...
- THUSC2013
魔塔 BZOJ 设每个敌人的属性值为\(hp_i,atk_i,def_i\).自己的为\(HP,ATK,DEF\) 首先我们可以发现顺序是没有影响的. 然后我们可以发现合适的\(ATK\)一定满足\( ...
- LC 1. Two Sum
题目介绍 Given an array of integers, return indices of the two numbers such that they add up to a specif ...
- 笔记-4:python组合数据类型
1.字符串(str) 字符串是字符的序列表示, 根据字符串的内容多少分为单行字符串和多行字符串. 单行字符串可以由一对单引号(') 或双引号(")作为边界来表示, 单引号和双引号作用相同. ...
- VBA学习资料分享-2
想利用VBA自动创建/发送OUTLOOK邮件,可以借助MailItem的Body属性或HTMLBody属性,代码模板如下: Dim objOutlook As Outlook.Application ...
- Javascript中new的作用
关于js中new关键字的理解,先来看个例子:像这样创建实例时使用new与不使用new有什么区别????function ParasiticPerson(name, age, job) { var ...
- 关于SpringMVC拦截器和异常
一.文件上传 1.文件上传 SpringMVC为文件上传提供了直接的支持,这种类型是通过即插即用的MultipartResolver技术的.Spring用Jakarta Commons FileUpl ...
- canvas之五角星的绘制
<html> <head> <meta charset=utf-8> <title>绘制简单图形线及矩形</title> <style ...
- 垃圾分类常见APP
垃圾分类指南app 上海就要实行垃圾分类了,垃圾分类指南app你需要吗,这里有相关的各种垃圾分类的介绍与上海垃圾分类投放指南,这里是垃圾分类指南手机入口能够让你更好的去完成垃圾分类呢.垃圾分类指 .. ...
- docker第二篇 Docker基础用法
Docker中的容器 lxc -> libcontainer -> runC OCI (Open Container Initiative) 由Linux基金会主导于2015年6月创立 作 ...