[golang note] 类型系统
值和引用
• 值语义和引用语义
值语义和引用语义的差别在于赋值:
b = a
b.Modify()
如果b的修改不会影响a的值,那么属于值类型,否则属于引用类型。
• 值类型和引用类型
√ 引用类型一个特点:引用不绑定特定对象(c++中引用是要绑定特定对象),例如有两个同类型引用a和b,它们可以引用各自的对象A和B;但如果a和b的引用都指向A,那么通过b修改对象内容可以反应到a引用之中。
√ golang从本质上说,一切皆是值类型,并没有内建一个类似java或c#等语言中的reference类型。
√ golang可以使用指针,可以在行为上达到类似java或c#等语言中reference的效果。
√ golang中从行为表现上看,数组属于值类型,数组切片、字典、通道和接口属于引用类型。
▶ 引用类型实现
√ golang中通过指针能实现引用类型效果。
√ golang中通过指针或对象访问成员都是使用点操作符,因此指针看起来和对象一样,即类似引用。
package main import (
"fmt"
) type Rect struct {
width, height float64
} func main() {
var a Rect = Rect{, }
var b *Rect = &a b.width =
b.height = fmt.Println(a.width, a.height) // 300 400
}
▶ 数组
√ golang中从行为表现上看,数组属于值类型。
package main import (
"fmt"
) func main() {
var a []int = []int{, , }
var b []int = a b[]++ fmt.Println(a) // [1 2 3]
fmt.Println(b) // [1 3 3]
}
▶ 数组切片
√ 我们大致可以将数组切片[]T抽象表示为:
type slice struct {
first *T
len int
cap int
}
√ golang数组切片本质上是一个含有存储指针的结构体,因此本质上说它是值类型,但从表现行为上看是一个引用类型。
package main import (
"fmt"
) func main() {
var a []int = []int{, , }
var b []int = a b[]++ fmt.Println(a) // [1 3 3]
fmt.Println(b) // [1 3 3]
}
▶ 字典类型
√ 我们大致可以将字典map[K]V抽象为:
type Map_K_V struct {
// ...
}
type map[K]V struct {
impl *Map_K_V
}
√ golang字典本质上是一个含有字典指针的结构体,因此本质上说它是值类型,但从表现行为上看是一个引用类型。
package main import (
"fmt"
) func main() {
var a, b map[string]string
a = make(map[string]string)
a["1"] = "haha"
a["2"] = "hehe" b = a
b["2"] = "shit" fmt.Println(a) // map[1:haha 2:shit]
fmt.Println(b) // map[1:haha 2:shit]
}
▶ 通道和接口
√ 与数组切片和字典类似,通道和接口本质上说是值类型,但从行为表现上看属于引用类型。
结构体(struct)
• 结构体定义
golang中的结构体(struct)和其他语言中的类(class)有同等地位。不同的是,golang放弃了包括继承在内的大量面向对象特性,只保留了组合这个最基础特性。
▶ 语法如下
type 结构体名 struct {
// 成员定义
}
• 构造和初始化
▶ new方式
▪ new函数原型
http://godoc.golangtc.com/pkg/builtin/#new
func new(Type) *Type {
...
}
▪ 语法如下
// 创建对象实例
p := new(Type) // 初始化代码
p.xxxx = xxxx
p.xxxx = xxxx
...
▪ 示例如下
package main import (
"fmt"
) type Rect struct {
width, height float64
} func main() {
rect := new(Rect) rect.width =
rect.height = fmt.Println(rect.width, rect.height)
}
▶ { }方式
▪ 语法如下
// 对象类型
obj0 := Type{}
obj1 := Type{v1, v2, ..., vn}
obj2 := Type{k1: v1, k2: v2, ..., kn: vn} // 指针类型
ptr0 := &Type{}
ptr1 := &Type{v1, v2, ..., vn}
ptr2 := &Type{k1: v1, k2: v2, ..., kn: vn}
√ 在golang中,未进行显式初始化的变量将会被初始化为该类型的“零”值(bool:false, int:0, string:"")。
√ 在golang中,没有构造函数的概念,对象的创建通常交由一个全局的创建函数来完成。
▪ 示例如下
package main import (
"fmt"
) type Rect struct {
width, height float64
} func main() {
// 对象
rect0 := Rect{}
fmt.Println(rect0.width, rect0.height) // 0, 0 rect1 := Rect{, }
fmt.Println(rect1.width, rect1.height) // 100, 200 //rect2 := Rect{100} // error : too few values in struct initializer rect3 := Rect{width: , height: }
fmt.Println(rect3.width, rect3.height) // 100, 200 rect4 := Rect{height: }
fmt.Println(rect4.width, rect4.height) // 0, 200 // 指针
rect5 := &Rect{}
fmt.Println(rect5.width, rect5.height) // 0, 0 rect6 := &Rect{, }
fmt.Println(rect6.width, rect6.height) // 100, 200 //rect7 := &Rect{100} // error : too few values in struct initializer rect8 := &Rect{width: , height: }
fmt.Println(rect8.width, rect8.height) // 100, 200 rect9 := &Rect{height: }
fmt.Println(rect9.width, rect9.height) // 0, 200
}
• 指针的作用
√ 在golang中,结构体属于值类型,这就意味着跨函数传递某个对象将只能得到其副本,倘若要在另一个函数中修改对象的内容,那么结果只是修改了副本内容,原对象的内容将没有改变。
package main import (
"fmt"
) type Rect struct {
width, height float64
} func ModifyRect(r Rect) {
r.width =
r.height =
return
} func main() {
rect := Rect{, }
ModifyRect(rect)
fmt.Println(rect.width, rect.height) // 100, 200
}
√ 只有通过传递指针,才能跨函数修改对象内容。而由于访问对象成员无论是实例还是指针,使用的都是点操作符,这就带来了类型的隐蔽性,因此在创建对象和传递实例时,尽量使用对象指针。
package main import (
"fmt"
) type Rect struct {
width, height float64
} func ModifyRect(r *Rect) {
r.width =
r.height =
return
} func main() {
rect := &Rect{, }
ModifyRect(rect)
fmt.Println(rect.width, rect.height) // 1000, 1000
}
• 添加成员方法
√ 在golang中,可以给任意类型(包括内置类型,但不包括指针类型)添加相应的成员方法。
√ 在golang中,没有隐藏的this或self指针,即方法施加的目标对象将被显式传递,没有被隐藏起来。
√ 在golang中,成员对象定义时候指定作用的目标对象是对象实例还是对象指针。
√ 在golang中,无论是对象实例还是对象指针都可以调用成员函数,不管成员函数作用对象类型,但是不影响最后的结果,即结果只由作用的目标类型决定(是对象实例还是对象指针)。
▶ 对象实例传递
√ 对象实例传递,即非指针,按值传递。
√ 这种方式将无法在方法中修改对象实例的内容,因为按值传递得到是对象副本,这与函数参数性质一样。
▪ 语法如下
func (obj Type) 函数名 (参数列表) (返回值列表) {
// 函数体
}
▪ 示例如下
package main import (
"fmt"
) type Rect struct {
width, height float64
} func (r Rect) ModifyRect() {
r.width =
r.height =
fmt.Println(r.width, r.height) // 1000, 1000
} func main() {
rect1 := Rect{, }
rect1.ModifyRect()
fmt.Println(rect1.width, rect1.height) // 100, 200 rect2 := &Rect{100, 200}
rect2.ModifyRect() //指针依然可以调用,但不改变结果
fmt.Println(rect2.width, rect2.height) // 100, 200
}
▶ 对象指针传递
▪ 语法如下
√ 对象指针传递,可以修改作用对象的内容。
func (ptr *Type) 函数名 (参数列表) (返回值列表) {
// 函数体
}
▪ 示例如下
package main import (
"fmt"
"reflect"
) type Rect struct {
width, height float64
} func (r *Rect) ModifyRect() {
r.width =
r.height =
fmt.Println(r.width, r.height) // 1000, 1000
} func main() {
rect1 := Rect{, }
fmt.Println(reflect.ValueOf(rect1).Type()) // main.Rect
rect1.ModifyRect() // 使用对象实例调用依然有效
fmt.Println(rect1.width, rect1.height) // 1000, 1000 rect2 := &Rect{, }
fmt.Println(reflect.ValueOf(rect2).Type()) // *main.Rect
rect2.ModifyRect() // 使用对象指针调用依然有效
fmt.Println(rect2.width, rect2.height) // 1000, 1000
}
▶ 为已有类型添加成员方法
√ golang有个很酷的特性:可以通过给已有类型Type起一个别名Alias,然后为Alias增加一些新的方法使其成为一个新的类型,同时Alias将完全拥有Type的所有方法。
▪ 语法如下
type Alias Type
func (a Alias) 函数名(参数列表) (返回值列表) {
// 函数体
}
func (a *Alias) 函数名(参数列表) (返回值列表) {
// 函数体
}
▪ 示例如下
package main
import "fmt"
type Rect struct {
width, height float64
}
func (r *Rect) ModifyRect() {
r.width =
r.height =
}
type MyRect Rect
func (r *MyRect) GetArea() float64 {
return r.width * r.height
}
func main() {
rect1 := &Rect{, }
rect1.ModifyRect()
fmt.Println(rect1.width, rect1.height) // 1000 1000
fmt.Println((*MyRect)(rect1).GetArea()) // 1e+06 进行强制类型转换可以调用
// fmt.Println(rect1.GetArea()) // error : rect1.GetArea undefined (type Rect has no field or method GetArea)
rect2 := &MyRect{, }
// rect2.ModifyRect() // error : rect2.ModifyRect undefined (type *MyRect has no field or method ModifyRect)
(*Rect)(rect2).ModifyRect() // golang是强类型语言,这里调用要进行强制类型转换才能调用
fmt.Println(rect2.width, rect2.height) // 1000 1000
fmt.Println(rect2.GetArea()) // 1e+06
}
• 包可见性
√ golang要使某个符号对其他包可见,只需将该符号定义为以大写字母开头即可。这规则对于成员变量和成员函数是一致的。
▶ 示例如下
▪ src/rect/rect.go
package rect
type Rect struct {
Width, Height, area float64
}
func (rect *Rect) CalcArea() {
rect.area = rect.Width * rect.Height
}
func (rect *Rect) GetArea() float64 {
return rect.area
}
▪ src/main/main.go
package main import "fmt"
import "rect" func main() {
rect := &rect.Rect{Width: , Height: }
fmt.Println(rect.Width, rect.Height) // 100 200 rect.CalcArea()
// fmt.Println(rect.area) // error : implicit assignment of unexported field 'area' in rect.Rect literal
fmt.Println(rect.GetArea()) //
}
[golang note] 类型系统的更多相关文章
- [golang note] 环境搭建
LiteIDE(windows) • golang安装 ▶ 下载对应操作系统的版本并安装,下载地址:http://www.golangtc.com/download,譬如这里下载的是go1.6.win ...
- [golang note] 网络编程 - RPC编程
net包 • 官方文档 http://godoc.golangtc.com/pkg/net/ Package net provides a portable interface for network ...
- [golang note] 工程组织
golang项目目录结构 <golang_proj> ├─README ├─AUTHORS ├─<bin> ...
- [golang note] 协程通信
channel基本语法 • channel介绍 √ golang社区口号:不要通过共享内存来通信,而应该通过通信来共享内存. √ golang提供一种基于消息机制而非共享内存的通信模型.消息机制认为每 ...
- [golang note] 协程基础
协程概念 √ 协程通常称为coroutine,在golang中称为goroutine. √ 协程本质上是一种用户态线程,它不需要操作系统来进行抢占式调度,在实际实现中寄存在线程之中. √ 协程系统开销 ...
- [golang note] 接口使用
侵入式接口 √ 在其他一些编程语言中,接口主要是作为不同组件之间的契约存在,即规定双方交互的规约. √ 对契约的实现是强制的,即必须确保用户的确实现了该接口,而实现一个接口,需要从该接口继承. √ 如 ...
- [golang note] 匿名组合
匿名组合 golang也提供了继承机制,但采用组合的文法,因此称为匿名组合.与其他语言不同, golang很清晰地展示出类的内存布局是怎样的. • 非指针方式组合 ▶ 基本语法 // 基类 type ...
- [golang note] 错误处理
错误处理 • 错误处理的标准模式 √ golang错误处理的标准模式:error接口. √ golang函数如果要返回错误,规范上是将error作为多返回值中的最后一个,但这并非是强制要求. ▶ er ...
- [golang note] 函数定义
普通函数定义 √ golang函数基本组成:关键字func.函数名.参数列表.返回值.函数体和返回语句. • 语法如下 func 函数名(参数列表) (返回值列表) { // 函数体 } • 示例如下 ...
随机推荐
- jquery1.7+里不能用checked获得checkbox的属性
jquery1.7+以后用.attr('checked')得到的,都是undefined. 结论就是.attr()不能用于普通对象,数组,窗口,文档.要重新获取改变的dom属性,需要用.prop()方 ...
- 杂文 - 设计MIUI主题 的 MIUI设计师
设计MIUI主题 的 MIUI设计师 本文地址: http://blog.csdn.net/caroline_wendy 时间: 2014.6.10 By Spike. 1. 首先注冊MIUI设计师: ...
- 如何编写jQuery插件
要说jQuery 最成功的地方,我认为是它的可扩展性吸引了众多开发者为其开发插件,从而建立起了一个生态系统.这好比大公司们争相做平台一样,得平台者得天下.苹果,微软,谷歌等巨头,都有各自的平台及生态圈 ...
- uva 11381(神奇的构图、最小费用最大流)
题目链接:http://acm.hust.edu.cn/vjudge/problem/viewProblem.action?id=29821 思路:首先拆点,每个字符对应的位置拆成i和i+len,然后 ...
- IOS 开发之 -- 过滤掉字符串里面所有的非法字符 字典和json之间的互转
比如一个字符串: NSString * hmutStr = @"(010)*(123)E6(234)**150-1111-^^-1234#" 很多时候,数据之间的传输,我们仅仅只想 ...
- 在scrollview中双击定点放大的代码
双击放大是 iPhone 的一个基本操作,第三方程序里引入这一功能的话,主要是在 scrollview 呈现一张图片或者 PDF 页面时,双击可以放大,主要代码如下 - (void)scrollVie ...
- 《Shiro框架》shiro学习中报错解决方法
[1] 最近在学习shiro,在学习过程中出现了一个问题,报错如下: org.apache.shiro.UnavailableSecurityManagerException: No Security ...
- jquery做简单特效
1.点击触发消失效果 <!DOCTYPE html> <html> <head> <meta charset="utf-8"> &l ...
- 【黑金原创教程】 FPGA那些事儿 SignalTap II 调试技巧
简介工具篇系列的第三本教程,讲述各种与SignalTap II 有关的调试技巧. 目录[黑金原创教程] FPGA那些事儿<工具篇III>:File01 - 上线调试与下线调试[黑金原创教程 ...
- Assembly中Load, LoadFrom, LoadFile以及AppDomain, Activator类中相应函数的区别
Assembly和AppDomain的一些关于动态加载程序集的函数有些令人头疼,但细细研究后还是可以将他们区分的. 这些函数大致可以分为四类: 第一类:加载到Load Context内 Load Co ...