一、函数

Go中函数是一等(first-class)类型。我们可以把函数当作值来传递和使用。Go中的函数可以返回多个结果。
  函数类型字面量由关键字func、由圆括号包裹声明列表、空格以及可以由圆括号包裹的结果声明列表组成。其中参数声明列表中的单个参数声明之间是由英文逗号分隔的。每个参数声明由参数名称、空格和参数类型组成。参数声明列表中的参数名称是可以被统一省略的。结果声明列表的编写方式与此相同。结果声明列表中的结果也是可以被省略的,并且在只有一个无名称的结果声明时还可以省略括号。例:

func (input1 string, input2 string)
string

这一类型字面量表示了一个接受两个字符串类型的参数且会返回一个字符串类型的结果的函数。如果我们在它的左边加入type关键字和一个标识符作为名称的话就变成了一个函数类型声明:

type MyFunc func(input1 string, input2 string)
string

函数值(或简称函数)的写法与此不完全相同。编写函数的时候需要先写关键字func和函数名称,后跟参数声明列表和结果声明列表,最后是花括号包裹的语句列表。例如:

func myFunc(part1 string, part2 string)
(result string) {result = part1 + part2 return }

如果结果声明是带名称的,那么它就相当于一个已被声明但未被显示赋值的变量。我们可以为它赋值且在return语句中省略掉需要返回的结果值。该函数还有一种更常规的写法:

func myFunc(part1 string, part2 string)
string { return part1 + part2 }

函数myFunc是函数类型MyFunc的一个实现。实际上,只要一个函数的参数声明列表和结果声明列表中的数据类型顺序和名称与某一个函数类型完全一致,前者就是后者的一个实现。
  我们可以声明一个函数类型的变量,如:

var splice func(string, string) string // 等价于var splice MyFunc

然后把函数myFunc赋给它:

splice = myFunc

如此一来,我们就可以在这个变量之上实施调用动作了:

splice("1", "2")

实际上,这是一个调用表达式。上面的代码可以简化为:

var splice = func(part1 string, part2 string)
string { return part1 + part2 }

在这个示例中,我们直接使用了一个匿名函数来初始化splice变量。顾名思义,匿名函数就是不带名称的函数值。匿名函数直接由函数类型字面量和由花括号包裹的语句列表组成。注意,这里的函数类型字面量中的参数名称是不能被省略的。
  还可以进一步简化--省去splice变量。例:

var result = func(part1 string, part2 string)
string { return part1 + part2 }("1", "2");

函数类型的零值是nil。

二、结构体和方法

Go语言的结构体类型Struct比函数类型更灵活。它可以封装属性和操作。前者即是结构体类型中的字段,而后者则是结构体类型所拥有的方法。
  结构体类型的字面量由关键字type、类型名称、关键字struct以及花括号包裹的若干字段声明组成,其中,每个字段声明独占一行并由字段名称(可选)和字段类型组成。例:

type Person struct {Name string Gender string Age uint8 }

结构体类型Person中有三个字段,分别是Name、Gender和Age。我们可以用字面量创建出一个该类型的值,像这样:

Person{Name:"Robert",Gender:"Male",Age:33}

如果这里键值对的顺序与其类型中的字段声明完全相同的话,可以统一省略所有字段的名称,就像这样:

Person{"Robert", "Male", 33}

我们在编写某个结构体类型的值字面量时可以只对它的部分字段赋值,甚至不对它的任何字段赋值。这时未被显式赋值的字段的值则为其类型的零值。
  与代表函数值的字面量相似。我们在写一个结构体值的字面量时不需要先拟好其类型。这样的结构体字面量被称为匿名结构体。与匿名函数类似,我们在编写匿名结构体时需要先写明其类型特征(包含字段声明),再写出它的值初始化部分。我们依照结构体类型Person创建一个匿名结构体:

p := struct {Name stringGender stringAge uint8
}{"Robert", "Male", 33}

匿名结构体最大的用处是在内部临时创建一个结构以封装数据,而不必正式为其声明相关规则。
  结构体类型可以拥有若干方法(匿名结构体是不可能拥有方法的)。所谓方法,其实就是一种特殊的函数。它可以依附于某个自定义类型。方法的特殊在于它的声明包含了一个接收者声明。这里的接收者指代它所依附的那个类型。以结构体类型Person为例,下面是依附于它的一个名为Grow的方法的声明:

func (person *Person) Grow() { person.Age++ }

如上所示,在关键字func和名称Grow之间的那个圆括号及其包括的内容就是接收者声明。其中的内容由两部分组成。第一部分是代表它所依附的那个类型的值的标识符。第二部分是它依附的那个类型的名称。后者表明了依附关系,而前者则使得在该方法中的代码可以使用到该类型的值(也称为当前值)。代表当前值的那个标识符可被称为接收者标识符,或简称为接收者。例:

p := Person{"Robert", "Male", 33}
p.Grow()

我们可以直接在Person类型的变量p之上应用调用表达式来调用它的方法Grow。注意,此时方法Grow的接收者标识符person指代的正是变量p的值。这也是“当前值”这个词的由来。在Grow方法中,我们通过使用选择表达式选择了当前值的字段Age。并使其自增。因此,在语句p.Grow()被执行之后,p所代表的那个人就又年长了一岁(p的Age字段的值已变成34)。
  在Grow方法的接收者声明中的那个类型是*Person,而不是Person。实际上,前者是后者的指针类型。这也使得person指代的是p的指针,而不是它本身。   结构体类型属于值类型。它的零值并不是nil。而是其中字段的值为相应类型的零值的值。结构体类型Person的灵值若用字面量来表示的话是Person{}

三、接口

在Go语言中,一个接口类型总是代表着某一种类型(即所有实现它的类型)的行为。一个接口类型的声明通常会包含关键字type、类型名称、关键字interface以及由花括号包裹的若干方法声明。例:

type Animal interface {Grow() Move(string) string }

接口类型中的方法声明是普通的方法声明的简化形式。它们只包括方法名称、参数声明列表和结果声明列表。其中的参数的名称和结果的名称都可以被省略。未省略应该是这样的:

Move(new string) (old string)

如果一个数据类型所拥有的方法集合中包含了某一个接口类型中的所有方法声明的实现,那么就可以说这个数据类型实现了那个接口类型。所谓实现了一个接口中的方法是指,具有与该方法相同的声明并且添加了实现部分(由花括号包裹的若干条语句)。相同的方法声明意味着完全一致的名称、参数类型列表和结果类型列表。其中,参数类型列表即为参数声明列表中除去参数名称的部分。一致的参数类型列表意味着其长度以及顺序的完全相同。对于结果类型列表也是如此。   我们无需在一个数据类型中声明它实现了哪个接口。只要满足了"方法集合为超集"的条件,就建立了“实现”关系。   现在我们已经认为*Person类型实现了Animal接口。但是Go语言编译器是否也这么认为呢?这显然需要一种显式的判定方法。在Go语言中,这种判定可以用类型断言来实现。不过,在这里,我们不能在一个非接口类型的值上应用类型断言来判定它是否属于某一个接口类型。我们必须先把前者转换成空接口类型的值。这就涉及到了Go的类型转换。
  Go语言的类型转换规则定义了是否能够以及怎样把一个类型的值转换另一个类型的值。另一方面,所谓空接口类型即是不包含任何方法声明的接口类型,用interface{}表示,常简称为空接口。Go中的包含预定义的任何数据类型都可以被看作是空接口的实现。我们可直接使用类型转换表达式把一个*Person类型转换成空接口类型的值,例:

p := Person{"Robert", "Male", 33, "Beijing"} v := interface{}(&p)

在类型字面量后跟由圆括号包裹的值(或能够代表它的变量、常量或表达式)就构成了一个类型转换表达式,意为将后者转换为前者类型的值。在这里,我们把表达式,意为将后者转换为前者类型的值。在这里,我们把表达式&p的求值结果转换成了一个空接口类型的值,并由变量v代表。表达式&p(&是取地址符)的求值结果是一个*Person类型的值,及p的指针。
  在这之后,我们就可以在v上应用类型断言了,即:

h,ok := v.(Animal)

类型断言表达式v.(Animal)的求值结果可以有两个。第一个结果是被转换后的那个目标类型(这里是Animal)的值,而第二个结果则是转换操作成功与否的标志。显然,ok代表了一个bool类型的值。它也是这里判定实现关系的重要依据。

四、指针

指针操作涉及到两个操作符--&和*。这两个操作符均有多个用途。但是当它们作为地址操作符出现时,&的作用是取址,*的作用是取值。   当*出现在一个类型之前(如*Person和*[3]string)时就不能被看作是操作符了,而应该被视为一个符号。如此组合而成的标识符所表达的含义是作为第二部分的那个类型的指针类型。我们也可以把其中的第二部分所代表的类型称为基底类型。   现在后头看结构体类型Person。它及其两个方法的完整声明如下:

type Person strcut {Name    stringGender  stringAge     uint8Address string
}func (person *Person) Grow() { person.Age++ } func (person *Person) Move(newAddres string) string { old := person.Address person.Address = newAddress return old }

注意,Person的两个方法Grow和Move的接收者类型都是*Person,而不是Person。只要一个方法的接收者类型是其所属类型的指针类型而不是该类型本身,那么就可以称该方法为一个指针方法。上面的Grow方法和Move方法都是Person类型的指针方法。
  相对的,如果一个方法的接收者类型就是其所属的类型本身。那么我们就可以把它叫做值方法。

func (person Person) Grow() { person.Age++ }

方法的接收者标识符所代表的是该方法当前所属的那个值的一个副本,而不是该值本身。   之所以表达式person.Age成立,是因为如果Go语言发现person是指针并且指向的那个值有Age字段,那么就会把该表达式视为(*person).Age。

拥有指针方法Grow和Move的指针类型*Person是接口类型Animal的实现类型,但是它的基底类型Person却不是。这样的表象隐藏了另一条规则:一个指针类型拥有以它以及以它的基底类型为接收者类型的所有方法,而它的基底类型却至拥有以它本身为接收者类型的方法。

  我们在基底类型的值上仍然可以调用它的指针方法。例如,若我们有一个Person类型的变量bp,则调用表达式bp.Grow()是合法的。这是因为,如果Go语言发现我们调用的Grow方法是bp的指针方法,那么它会把调用表达式是为(&bp).Grow()。

转载于:https://www.cnblogs.com/orlion/p/6574366.html

Golang 笔记 2 函数、结构体、接口、指针相关推荐

  1. go语言----函数 结构体 接口 多态

    函数 Go语言 函数是反过来声明 变量类型和  函数返回值 一.一个返回值 package main import "fmt"func max(a int,b int) int { ...

  2. 在Golang里如何实现结构体成员指针到结构体自身指针的转换

    原文地址:http://goworldgs.com/?p=37 在C语言中有一个经典的宏定义,可以将结构体struct内部的某个成员的指针转化为结构体自身的指针.下面是一个例子,通过FIELD_OFF ...

  3. lru算法C语言结构体指针,在Golang里如何实现结构体成员指针到结构体自身指针的转换...

    在C语言中有一个经典的宏定义,可以将结构体struct内部的某个成员的指针转化为结构体自身的指针.下面是一个例子,通过FIELD_OFFSET宏计算结构体内一个字段的偏移,函数getT可以从一个F*的 ...

  4. JNA实战笔记汇总(二)——JNA和C / C ++的数据类型映射(dll函数回调、结构体、指针)

    目录 JNA技术难点 1.函数回调 2.结构体 3.指针 JNA技术难点 有过跨平台.跨语言开发的程序员都知道,跨平台.预研调用的难点,就是不同语言之间数据类型不一致造成的问题.绝大部分跨平台调用的失 ...

  5. 换个语言学一下 Golang (9)——结构体和接口

    换个语言学一下 Golang (9)--结构体和接口 基本上到这里的时候,就是上了一个台阶了.Go的精华特点即将展开. 结构体定义 上面我们说过Go的指针和C的不同,结构体也是一样的.Go是一门删繁就 ...

  6. c语言 返回函数是结构体指针变量,一个函数返回值为指向结构体的指针的问题...

    一个函数返回值为指向结构体的指针的问题 #include #include struct student { int num; char name[10]; struct student *next; ...

  7. C语言项目实战:24点游戏计算器(基于结构体、指针、函数、数组、循环等知识点)

    文章目录 C语言项目实战:24点游戏计算器(基于结构体.指针.函数.数组.循环等知识点) 前言:24点游戏计算器的规则如下 一.项目的创建标 1.选择项目 2.输入项目名称和路径 3.创建头文件Mai ...

  8. C 语言结构体数组指针以及函数

    结构体数组指针 指针变量可以指向一个结构体数组,这时指针变量的值是整个数组的首地址. 设 ps 为指向结构体数组的指针变量,则 ps 也指向该结构体数组的第 0个元素,ps+1 指向第一个元素,ps+ ...

  9. 专科 java转go 翱翔之路(一)基础语法:变量声明,匿名函数,结构体,函数,map

    本人专科!在太原干了一年了,想从java转go,上班中自学go,明年找go语言相关的工作 立帖为证! 待羽翼丰满,大风到来,便是我翱翔之时!!! 1.Go语言 1.命令 1.1 查看版本号 go ve ...

  10. 初识Golang的面向对象 为结构体(类)绑定方法

    前言 Go语言面向对象 Go语言仅支持封装 不支持继承和多态 继承和多态通过接口实现 成员函数的实现 创建类对象在Go语言中表现为创建一个结构体变量 可以定义一个工厂函数 专门负责初始化结构体(类)变 ...

最新文章

  1. 用canvas实现一个vue弹幕组件
  2. 学会和同事相处的30个原则
  3. Google分布式系统三驾马车: GFS,mapreduce,Bigtable
  4. 字符串匹配(KMP 算法 含代码)
  5. LeetCode 1334. 阈值距离内邻居最少的城市(最短路径Dijkstra)
  6. html-css样式表
  7. 有序集合使用与内部实现原理
  8. python电影推荐系统的设计与实现_一种电影推荐系统的设计与实现
  9. jquery 检查 金钱类型
  10. Java校招笔试题-Java基础部分(一)
  11. java并发编程:设计原则与模式_java编程设计模式一——策略模式
  12. 卸载loadrunner
  13. 11月3日云栖精选夜读:《maven实战》读书笔记2——maven安装(windows和eclipse插件)...
  14. Flutter之Binding简单梳理
  15. com.netflix.zuul.exception.ZuulException: Forwarding error
  16. LeetCode 981.基于时间的键值存储(C++)
  17. DataStream API:Event Time(Generating Watermarks)
  18. 一种基于波状扩散特征分析的光斑检测方法
  19. VENUE | S6L初次安装详细视频指南
  20. 解决adb无法连接夜神模拟器的问题

热门文章

  1. LINQ简记(3):子句
  2. exchange实现外部邮件收发的两种方法
  3. 拿着6位数工资,没写一行代码!为何还能成为最佳员工?
  4. linux命令总结之tr命令
  5. java websocket
  6. 使用Cobbler安装多版本操作系统
  7. Linux的cron和crontab
  8. Linux学习之CentOS(三十三)--DNS基础及域名系统架构
  9. 《版式设计——日本平面设计师参考手册》—第1章段落样式和字符样式的基础知识...
  10. Spring 事务 状态信息的创建、回滚、清理、提交