八,struct
struct的定义如下: type 结构体名 struct{},其中定义的变量不要var,但是仍然是倒序。结构体变量的初始化:结构体名{}括号中的变量按照定义的顺序依次填写;如果不想写全,或者不想按顺序写,还可以写成json的格式。(这个为后续添加变量,而无需修改赋值提供了方便);
匿名变量的访问:在S1中有一个匿名变量S,对于S中的变量的访问可以直接写a.name如13行所示;当然也可以写成a.S.name(其变量明就是S); 如果S1中又定义了name,写全可以访问到S中的变量了;
由于*S和S中的变量的访问方法相同,所以不能同时存在一个S一个*S的匿名变量;
点击(此处)折叠或打开
- package main
- type S struct{
- age int
- name string
- }
- type S1 struct{
- S
- }
- func main()
{
- var a S1=S1{S{10,"tom"}}
- // var a S1=S1{S{name:"tom"}}
- println(a.name);
- }
九 函数&方法
函数的声明和C也是差不多的。就是变量的类型和返回值都是写在后面的。还有可以有多个返回值哦。当函数带上receiver就变成方法了,如第10行所示;
点击(此处)折叠或打开
- package main
- type iface interface {
- setName()
- }
- type S1 struct {
- name string
- }
- func (s *S1) setName()
{
- s.name =
"hello"
- }
- func main()
{
- var a S1
- (*S1).setName(&a)
//a.setName()
- println(a.name)
- }
这就a.setName的调用发发有点类似面向对象的写法了。如果将地10行的*去掉。则调用该函数是会创建一个临时的S1对象。让后再对其赋值。等函数退出后,之际调用的便量并没有得到修改。那么这个函数还有什么作用呢? 为什么还要提供写法的?
当然由于slice的特殊性,当slice作为参数时,其内容被更改了,还是可以反应到实际数组中去的。如下面的代码,实际打印结果是10;因为此处传值指示传的slice本身,而其指向的数组还是同一个。要想让b指向另一个数组,就必须得用指针型了。
还有注意点,就是原始类型不能直接作为receiver;
点击(此处)折叠或打开
- package main
- type A []int
- func (s A) setValue()
{
- s[0]
= 10
- }
- /*func
(s *A) setValue()
{
- (*s)[0]
= 10 //注意,此处必须带*号,编译器已经无法自动转换了。如上例的第11行;
- }*/
- func main()
{
- var a = [...]int{1,
2, 3, 4, 5, 6, 7, 8}
- b := A(a[1:6])
- b.setValue()
- println(a[1])
- }
十,interface
上面的例子里面已经用到了interface;和java相同,interface是一个定义了一系列方法的集合。但是与java不同的是,此处的interface不需要明确标明implents,只要一个类型的被作为receiver实现了所有的interface的方法,编译器就能自动识别该类型实现了该interface;如下面的代码:S1实现了myface;由代码的19,20行可以发现,无论是送入S1的指针,还是对象,都能实现对setName的调用,如果setName的receiver是*S;则20行会报错。这就应证了golang手册中的一句话,T的方法集包含receiver为T 和*T的所有方法,而*T的方法集只包含receiver为*T的方法;(更通俗的表达方法时,当参数(receiver是T)时,调用该方法的对象既可以时T,也可以时*T; 当receiver为*T时,调用时的参数只能时*T; 所以当interface作为参数时,具体是传入T还是*T。需要看该对象的具体实现,如果该对象的receiver只有*T,则参数只能传入*T,否则可以任选T或者*T; 当然只有当receiver为*T,且参数传入*T时,才可能真正修改到T对象的成员变量;
点击(此处)折叠或打开
- package main
- type myface interface {
- setName()
- }
- type S1 struct {
- name string
- }
- func (s S1) setName()
{
- s.name =
"hello"
- }
- func test(a myface)
{
- a.setName()
- }
- func main()
{
- var a, b S1
- test(&a)
- test(b)
- println(a.name)
- println(b.name)
- }
当20行的掉用进入到14行时:a的内容为: {tab = 0xc200035030, data = 0xc20001b020},data是一个紧依赖于上面的一个空间。
由上面两个联合得知在19行运行info locals的输出。
(gdb) i locals
b = {name = {str = 0x0, len = 0}}
&a = 0xc20001b010
以及:p &b.name
$11 = (struct string *) 0x7fffe7f73f58
结合上面的结果可以看出此处输出的&a不是a的地址,而是某个中间状态得东西,这个和test(b)进入test函数后的内容类似;
经过查看内存发现 当今如14行时,此处的a的大小是固定的只包含两个指针的变量,所以sizeof(a)=2*sizeof(void*);而*(a.data)是一个内容空间,而该空间实际是test,传值时的一个拷贝;
下面在看看*T的receiver的结果:
点击(此处)折叠或打开
- package main
- type myface interface {
- setName()
- }
- type S1 struct {
- name string
- age int
- }
- func (s* S1) setName()
{
- s.age=11
- }
- func test(a myface)
{
- a.setName()
- }
- func main()
{
- var a, b S1
= S1{name:"tom"}, S1{name:"java"}
- test(&a)
- test(&b)
- println(a.name)
- println(b.name)
- }
点击(此处)折叠或打开
- package main
- type myface interface {//接口,只有一个方法
- setName()
- }
- type S1 struct {
//结构体S1
- name string
- }
- type S2 struct {
//结构体S2
- name string;
- }
- func (this S2)setName(){
//S2的receiver是T;
- this.name="s2";
- }
- func (s *S1) setName()
{ //S1的receiver是*T;
- s.name =
"S1"
- }
- func test1(a myface){
- a.setName();
//调用接口的方法;
- }
- func main(){
- a2:=S2{name:
"aS2"}
- a1:=S1{name:"aS1"};
- //b1:=S1{name:
"bS1"}
- b2:=S2{name:"bS2"}
- test1(&a1)
//S1的receiver是*T,所以值接受*T参数;
- //test1(b1)
- test1(&a2)
//S2的receiver是T,所以接受*T;
- test1(b2)
//S2的receiver是T,所以也接受T;
- println("a1.name=",a1.name);
//S1,这是因a1的内存地址最终能传到*S1.setName;
- println("a2.name=",a2.name);
//aS2(没有变),a2的地址虽然能到达test1,但是S2.setName中又复制了一个变量;
- println("b2.name=",b2.name);
//bS2(没有变),b2到达test1时已经复制了一份,但是到s2的setName又复制了,总计复制两遍。肯定达不到效果了
- }
详细参见:http://golang.org/doc/articles/laws_of_reflection.html; 或者其中的blog:http://research.swtch.com/interfaces
有疑问加站长微信联系(非本文作者)