邻里街坊 golang入坑系列
如果要追新或者怀旧,就点击https://andy-zhangtao.gitbooks.io/golang/content/ 。 博客园里面的文章基本和gitbook上面是保持同步的。
这几天看了几集<情满四合院>,这帮老演员演得真不错。也就不对标那些个小鲜肉了,他们除了长得好看,绯闻多。除此之外,没啥可关注的。老演员的一个眼神,一个动作都透着一股劲。这股劲能把人带到戏里面去,能让观众情不自禁的把自己带到那种氛围里面。 好像看的并不是别人家的事情,而是自己家的家长里短。 轮台词,没有华丽的辞藻。轮布景,就是在影棚里面搭出来的景。论演员,刚才说了,没有好看的小鲜肉。 轮特效,没有!论剧情,家长里短,好不出彩。可为啥能打动人,应该是演员的"敬业"精神吧。每个演员都把自己的角色演活了,演的非常生动,非常立体。就好像这些个演员都出自身边环境一样,没有丝毫的距离和陌生感。这种好片子太少了,好演员也太少,因此敬业的精神就显得越发珍贵了。
本节来聊Golang中的结构体(Struct)。Golang的缔造者绝对对C有着很深的感情,Golang从里到外都透着C的感觉。就感觉Golang是C的豪华升级版。结构体,又名Struct。除了在C/C++之外,很少会使用到。这里暂且不谈Struct在C里面的使用方式,只聊在Golang中应该如何使用。
首先来看Struct在Golang中处于什么作用。前文说过Golang中有几种基本数据类型,背不出来,屁股要挨打的哦😔。但凡写过代码的人都清楚,只靠这几种数据类型是无法满足业务逻辑需要的,成熟的语言一定会满足用户自定义数据类型的需要。 在Golang当中,Struct就是为了满足自定义数据类型而诞生的。如果非要类比,那Struct就可以对标成Java里面的Class。比如下面:
type News struct{
Title string
Content string
Date string
}
News就是一个标准的Struct,里面有三个属性。由此,可以推导出Struct的声明语法:
type name struct {
member definition;
member definition;
...
member definition;
}
name是struct的名称,也可以理解成类名。member可以理解成成员变量,这里要注意,如果首字母大写,则属于public。如果小写,则属于private。而definition则是变量类型,这个类型可以是基本类型也可以是自定义类型,总之是合法数据类型就可以。
一旦声明完成,恭喜你,你拥有了一个独属于你的数据类型。但怎么使用呢?先来看初始化。最简单的方式:
myNews := News{
Title:"xxx",
Content:"xxxx",
Date:"xxxx",
}
这是最简单的初始化方式,其中不是每个变量都需要给值的,想给几个就给几个,想不给,那就直接 myNews := News{}。高兴就好!
初始化之后,就轮到使用了。 怎么用?最简单的方式:myNews.Title就可以了。 如果高兴,也可以仿照Java来个Setter/Getter函数。没关系,不用担心别人说三道四。黑猫白猫,最合适的就是好猫。只要结构清晰,团队成员看起来舒服,学习成本低。爱用哪个就用哪个。
到此,你就可以随心所欲,敞开了用成员变量。甭担心会出错,年轻人写代码不出错还叫年轻人嘛!反正错了在fix呗。如果说看到这里,你就认为Struct说完了,那就找个地方泡杯茶,然后再回来吧。 注意到没有,上面所有的内容都是变量,说白了,还没涉及到成员函数呢。没有函数的Struct,就是一具没有骨骼的肉体,所以下面上骨骼。
和Java不同,Golang的Struct函数不是定义在Struct内部的,而是在外部。例如:
type News struct{
Title string
Content string
Date string
}
func (news *News)GetTitle(){....}
GetTitle就是News的成员函数,除了News的实例能调用。其它人无权调用。这里的写法是不是和以前讲解的函数声明方式不同?也没多什么,就多了一个"归宿"而已。我们告诉编译器,这个函数是属于News的。
在有的团队中(可能是Java转型过来的吧),喜欢声明成这样:
func (this *News)GetTitle(){ this.xxxx .... }
这样使用this会比较顺手。由此看出,news也好,this也罢。只是一个指代当前实例的指针名称而已。 叫啥都行,哪个顺手用哪个。反正只在函数内部生效。
有没有人会提问?这些都是实例方法(Java的说法),有没有静态方法?有呀!,把(this *News)拿掉,函数首字母大写,就是静态方法了。 也就是在此之前,我们一直在写的那些个代码。
Struct在Golang当中,说重要其实也不重要。因为就是一个自定义数据类型而已,无非是添加了一些成员变量,还有成员方法,只要注意首字母大小写就没问题了。 但如果说不重要吧,也有失偏颇,Golang代码中的大部分都是依靠各种各样的Struct来实现的。 因此对于Struct来说,战略上藐视,战术上重视比较合适。
因为Struct也是一种数据类型,所以以前说的各种变量使用场景,这里都适用。比如,将一个Struct当做参数传入函数中:
package main
import "fmt"
type Books struct {
title string
author string
subject string
book_id int
}
func main() {
var Book1 Books /* Declare Book1 of type Book */
var Book2 Books /* Declare Book2 of type Book */
/* book 1 specification */
Book1.title = "Go Programming"
Book1.author = "Mahesh Kumar"
Book1.subject = "Go Programming Tutorial"
Book1.book_id = 6495407
/* book 2 specification */
Book2.title = "Telecom Billing"
Book2.author = "Zara Ali"
Book2.subject = "Telecom Billing Tutorial"
Book2.book_id = 6495700
/* print Book1 info */
printBook(Book1)
/* print Book2 info */
printBook(Book2)
}
func printBook( book Books ) {
fmt.Printf( "Book title : %s\n", book.title);
fmt.Printf( "Book author : %s\n", book.author);
fmt.Printf( "Book subject : %s\n", book.subject);
fmt.Printf( "Book book_id : %d\n", book.book_id);
}
再比如结合分水岭中的指针,对Struct进行指针操作:
package main
import "fmt"
type Books struct {
title string
author string
subject string
book_id int
}
func main() {
var Book1 Books /* Declare Book1 of type Book */
var Book2 Books /* Declare Book2 of type Book */
/* book 1 specification */
Book1.title = "Go Programming"
Book1.author = "Mahesh Kumar"
Book1.subject = "Go Programming Tutorial"
Book1.book_id = 6495407
/* book 2 specification */
Book2.title = "Telecom Billing"
Book2.author = "Zara Ali"
Book2.subject = "Telecom Billing Tutorial"
Book2.book_id = 6495700
/* print Book1 info */
printBook(&Book1)
/* print Book2 info */
printBook(&Book2)
}
func printBook( book *Books ) {
fmt.Printf( "Book title : %s\n", book.title);
fmt.Printf( "Book author : %s\n", book.author);
fmt.Printf( "Book subject : %s\n", book.subject);
fmt.Printf( "Book book_id : %d\n", book.book_id);
}
如果猜的不错,你肯定是鼠标快速划过就完事了。 别走神,里面真有彩蛋。为啥提出这两个例子,是因为具体实践中,这是经常大意犯的错误。先看这两个例子的区别:
func printBook( book Books ) { ... }
func printBook( book *Books ) { ... }
一个是值传递,一个是引用传递。 因此在第一个例子中,如果printBook里面修改了数据,比如:
book.title = "lala"
外面仍然无法感知,因为是值传递。修改的是副本,不是本体。但如果在第二个例子中,修改了数据,外层就会收到影响,因为都是本体。
package main
import "fmt"
type Books struct {
title string
author string
subject string
book_id int
}
func main() {
var Book1 Books /* Declare Book1 of type Book */
var Book2 Books /* Declare Book2 of type Book */
/* book 1 specification */
Book1.title = "Go Programming"
Book1.author = "Mahesh Kumar"
Book1.subject = "Go Programming Tutorial"
Book1.book_id = 6495407
/* book 2 specification */
Book2.title = "Telecom Billing"
Book2.author = "Zara Ali"
Book2.subject = "Telecom Billing Tutorial"
Book2.book_id = 6495700
/* print Book1 info */
printBook(Book1)
fmt.Println(Book1.title)
/* print Book2 info */
printBook(Book2)
}
func printBook(book Books) {
book.title = "lala"
fmt.Printf("Book title : %s\n", book.title);
fmt.Printf("Book author : %s\n", book.author);
fmt.Printf("Book subject : %s\n", book.subject);
fmt.Printf("Book book_id : %d\n", book.book_id);
}
跑一下,看看结果就能看出区别了。
所以就这里需要注意一下,其它关于Struct的坑应该算是没有了吧。 如果有,恩,那就有吧。 坑坑何其多,岂能填的完。如果哪天我开公司,一定要招一个叫"田德湾"的员工,就冲着名字,吉利!
posted on 2017-12-01 10:27 vikings`s blog 阅读(450) 评论(0) 编辑 收藏 举报