Go 面向对象概念
前言: 本文是学习<<go语言程序设计>> -- 清华大学出版社(王鹏 编著) 的2014年1月第一版 做的一些笔记 , 如有侵权, 请告知笔者, 将在24小时内删除, 转载请注明出处!
1. 对象
- 任意简单的内置数据类型
- 任意复杂的结构体
- 表示具体的事物 / 抽象的规则 / 计划 / 事件 等.
2. 对象的状态
- 用数值来描述, 如长方体的长和宽等.
3. 对象的操作
- 用于改变对象的状态, 操作就是对象的行为.
- GO语言中称为Method(方法), Method 就是在 函数(function) 前面增加了一个接收者(Receiver)对象. 将操作和对象关联起来了.
- 定义: func (recv receiver_type) methodName(args)(rets){} Receiver 可以是: 内置类型/自定义类型/结构体/指针类型
- 多个Method可以同名, 只要接收者不同, 就是不同的Method.(类似于重载吧)
- Method可以访问接收者的字段, 而不需要将字段作为参数传入Method, 就像在struct中访问字段一样.
- 普通类型作为Receiver,是值传递; 指针类型作为Receiver, 将传递引用.
package main import (
"fmt"
"math"
) type rect struct {
width int
height int
}
type circle struct {
radius float32
} func (recv rect) area() int {
return recv.width * recv.height
}
func (recv circle) area() float32 {
return recv.radius * recv.radius * math.Pi
}
func main() {
r1 := rect{4, 3}
r2 := rect{30, 15}
fmt.Println(r1.area(), r2.area())
c := circle{5}
fmt.Println(c.area())
}
- Receiver
- 匿名Receiver, 省略了receiver的名字(类型没有省,所以可以判断), 此时不能定义同名的方法(应该是会造成无法将操作和对象绑定).
- Method 继承
- go中可以通过匿名字段实现字段继承; 如果匿名字段实现了一个Method(或者说是这个Method的Receiver), 那么包含这个匿名字段的struct对象也能调用该Method.
- 可以重写继承的方法, 对象会像处理匿名字段一样, 优先处理外部同名Method.
4. 接口(Interface)
- 一组Method的组合, 可以通过Interface来定义对象的一组行为, 如果某个对象实现了某个接口的所有方法, 那么它就实现了该接口. 无须显式在该类型上添加接口说明.
- 习惯以 "er" 结尾, 如: Printer, Reader, Writer.
- 一个Interface中包含的Method不宜过多, 一般 0 - 3个
- Interface可以被任意的对象实现, 一个对象也可以实现多个 Interface.
- 接口组合
- 类似类型的匿名组合(如结构体, Method) ,接口也可以组合: 将一个/多个接口匿名嵌入另外一个接口中 , 就组合了接口.
type SpeakListener interface{
Speaker // Speaker为一个匿名接口
Learner // Learner 为一个匿名接口
}
- 空接口 : 任何数据类型都默认实现了空接口. interface {}
- 可以用来定义任意类型的参数和返回值. (有点儿像是void 和 void* 的用处吧) : 1. func f1(a interface{}){} 2. func f2(a ... interface{}){}
- 接口的执行机制和赋值
- 接口是引用类型.(如何理解呢? )
- 接口是可以被实例化的类型, 当定义一个接口类型变量时, 系统会为其分配内存, 并将赋给它的对象赋值到这个内存区域.
- 接口对象(由 Itab指针和data指针组成)
struct Iface{ // C语言中的表示
Itab * tab;
void * data;
};
- Itab : 依据data类型创建, 存储了接口动态调用的元数据信息, 其中包括data类型所有符合接口签名的方法地址列表. 使用接口对象调用方法时就从Itab中查找对应的方法, 并将 *data 作为Receiver参数传递给该方法.
- 编译器在构建 Itab时, 区分T 和 * T 方法集, 病从中获取接口实现方法的地址指针, 接口调用不会做Receiver自动转换, 目标方法必须在接口实现的方法集中. 接口方法集规则:
- T 仅仅拥有T类型的方法集, 而 *T拥有(T+*T)方法集
- 基于T实现方法, 表示同事实现了interface(T) 和interface(*T) 接口
- 基于(*T)实现方法, 就只能是对 interface(*T)实现接口
- 接口的定义与赋值
- 定义了一个interface的变量, 那么变量里面可以存储实现了这个interface的任意类型的对象.
- 多个对象同时实现了这个接口的话, 那么可以用这个接口作为类型, 定义一个slice, 那么就可以写出比较少的代码. ix = make([]Speaker,3)
- 结构体的匿名字段方法和接口转换
- 当接口类型是struct时, 这些struct可能有匿名字段, 而为这些匿名字段定义的方法也会被接口所继承.
- 接口之间可以相互包含. == > 超级(范围大) + 子集(范围小)
- 匿名字段方法
- 接口支持struct匿名字段实现的方法, 因为外层结构"继承"了匿名字段的方法集. 规则如下(好奇怪的规则, 没看懂, 先记下来?), S和T都是struct类型的.
- 如果S中嵌入匿名类型T, 则S的方法集包含T的方法集.
- 如果S中嵌入匿名类型* T, 则S的方法集包含*T的方法集(T+*T)
- 如果S中嵌入匿名类型 T 或 (我怀疑应该是"和") * T, 则 *S的方法集合包含 *T的方法集 (T+*T)
- 贴段没看明白的代码(可以正常运行)
package main
import (
"fmt"
)
type People struct{
Name string
}
type Teacher struct{
People
Department string
}
func (p People)SayHi(){
fmt.Println(p.Name)
}
type Speaker interface{
SayHi()
}
func main() {
p := People{"Roger"}
t := Teacher{People{"roger"},"CS"}
var is Speaker
is = &p // 为 *People定义了SayHi(),自然实现该接口 (Why?)
p.SayHi()
is.SayHi()
is = &t
is.SayHi()
}
- 接口转换
- 拥有超集的接口还可以被转换成拥有子集的接口, 这样子集(结构体)就可以很容易地访问超集(结构体)对象中的成员变量或者数据.
- 贴段代码
package main import (
"fmt"
)
type People struct {
Name string
Age int
}
type Student struct{
People
School string
}
type PeopleInfo interface{
GetPeopleInfo()
}
type StudentInfo interface{
GetStudentInfo()
GetPeopleInfo()
}
func (p People) GetPeopleInfo() {
fmt.Println(p)
}
func (s Student) GetStudentInfo(){
fmt.Println(s)
}
func main() {
var is StudentInfo = Student{People{"Roger",18},"Sichuan Uni"}
is.GetStudentInfo()
is.GetPeopleInfo()
//接口StudentInfo拥有PeopleInfo的全部方法签名, PeopleInfo属于StudentInfo的一个子集(可以从方法数量上看),
//所以可以直接将接口类型变量is 赋值给ip. 类似于
// Student是People的子类, 继承就是任何子类 is a 父类.
var ip PeopleInfo = is
ip.GetPeopleInfo()
}
- 接口类型推断
- 通过接口赋值, 可以知道接口类型变量中可以存储任意类型的数值. 要想反向知道接口类型变量里保存的具体对象类型, 就可以用接口类型推断.
- 两种方法
- Comma-ok断言
package main import (
"fmt"
) type People struct {
Name string
Age int
}
type Tester interface { // 定义空接口用于存储任意类型
} func main() {
p := People{"roger", 20}
it := make([]Tester, 4)
it[0], it[1], it[2], it[3] = 1, "Hello", p, true
for index, element := range it {
// 没看明白这个 ok 是干啥的?
if value, ok := element.(int); ok {
fmt.Printf("t[%d] is an int. value = %d\n", index, value)
} else if value, ok := element.(string); ok {
fmt.Printf("t[%d] is an string. value = %v\n", index, value)
} else if value, ok := element.(People); ok {
fmt.Printf("t[%d] is an People type. value = %v\n", index, value)
} else {
fmt.Printf("t[%d] is an unknown type.\n", index)
}
}
}
- switch测试
- 上面的代码换成switch case就是了.
5. 反射
- 标准库: reflect, 提供TypeOf() , ValueOf() 方法 从 interface{}接口对象中获取实际目标对象的类型和值信息.
- 返回的Type和Value有大量的方法, 常用的有kind() 和 Float()等.
package main import (
"fmt"
"reflect"
) type Student struct {
Id int
Name string
Sex bool
Grade float32
} func (s Student) SayHi() {
fmt.Println("Hi")
}
func (s Student) MyName() {
fmt.Println("My name is : ", s.Name)
} //反射处理函数
func StructInfo(o interface{}) {
t := reflect.TypeOf(o)
if k := t.Kind(); k != reflect.Struct {
fmt.Printf("This value is not a struct, it's %v.", k)
return
}
fmt.Println("Struct name is", t.Name())
fmt.Println("Fields of struct is : ")
v := reflect.ValueOf(o)
for i := 0; i < t.NumField(); i++ {
field := t.Field(i)
value := v.Field(i).Interface()
fmt.Printf(" %6s : %v = %v\n", field.Name, field.Type, value)
}
fmt.Println("Method of the struct is:")
// 获取方法的Name和Type信息
for i := 0; i < t.NumMethod(); i++ {
method := t.Method(i)
fmt.Printf("%6s : %v \n", method.Name, method.Type)
}
} func main() {
s := Student{10001, "Roger", false, 99.5}
StructInfo(s)
}
- 上面代码中注意:
- 定义结构体中的字段名和方法名时要大写, 否则反射编译器可能会报错.
- 反射会将匿名字段当作一个独立字段来处理, 如果要获取嵌入字段的Type 和Value信息, 必须使用索引路径. 通过Value类型的 FieldByIndex() 获取嵌入字段的索引路径. func (v Value) FieldByIndex(index []int) Value
- 如果对一个非Struct类型执行FieldByIndex()将会产生panic错误.
Go 面向对象概念的更多相关文章
- delphi 基础之二 面向对象概念初步
面向对象概念初步 •类自动生成 快捷键:ctrl+shift+c 1.类的定义 类是用户创建的数据类型,包括状态.表达式和一些操作.有3个组成部分,即字段.方法和属性.字段是类的内部数据变量,方法就是 ...
- C#入门(面向对象概念)
也许是看概念性的东西太多了,所以一看基本就明白,但实际并没有掌握,有待实操.反而是UML(统一建模语言)引起了我的兴趣,发现这东东很像建筑行业的图纸:有标准和约定,很方便专业人士看懂程序的架构和逻辑. ...
- O-C相关-03:面向对象概念的具体介绍
1.面向对象的概念 面向对象(object-oriented ;简称: OO) 至今还没有统一的概念,我这里把它定义为:按人们认识客观世界的系统思维方式,采用基于对象(实体)的概念建立模型,模拟客观世 ...
- 《Java从入门到放弃》JavaSE入门篇:面向对象概念(入门版)
要知道什么是面向对象,你首先要有个对象吧,所以······没有对象的可以回家洗洗睡了· 好吧,前面是开玩笑,要说明什么是面向对象,我们还是先 例子: 小呆:"小傻,你今天早餐吃的什么?&qu ...
- 面向对象【day07】:面向对象概念介绍(二)
本节内容 1.概念 2.特性 3.面向对象介绍 一丶概念 1.面向对象编程 OOP(Object-Oriented Programming)编程是利用“类”和“对象”来创建各种模型来实现对真实世界的描 ...
- Python入门之面向对象编程(一)面向对象概念及优点
概念 谈到面向对象,很多程序员会抛出三个词:封装.继承和多态:或者说抽象.一切都是对象之类的话,然而这会让初学者更加疑惑.下面我想通过一个小例子来说明一下 面向对象一般是和面向过程做对比的,下面是一个 ...
- 3-C#面向对象概念
本篇博客对应视频讲解 回顾 前两篇博文带大家快速的感知一下使用C#编写程序是怎样的过程,能实现什么样的功能.同时也提到了面向对象的概念.本篇文章就是更加详细的去将面向对象编程中常见的概念进行示例说明, ...
- Java基础教程(4)--面向对象概念
如果你之前从来没有使用过面向对象编程语言,那么在学习Java之前需要先理解几个有关面向对象编程的基本概念.这篇教程将会向你介绍对象.类.集成.接口和包的概念,以及这些概念是如何与现实世界相关联,并 ...
- java面向对象概念1
一.java面向对象学习的三条主线: 1.java类及类的成员:属性.方法.构造器:代码块.内部类 2.面向对象的三大特征:封装性.继承性.多态性.(抽象性) 3.其它关键字:this.super.s ...
- js面向对象概念解析
ECMAScript有两种开发模式: 1.函数式(过程化) 2.面向对象(OOP). 面向对象的语言有一个标志,那就是类的概念,而通过类可以创建任意多个具有相同属性和方法的对象.但是,ECMAScri ...
随机推荐
- java web 程序---在线时长
思路:toLocalString()这个方法 <body> <% long t=session.getLastAccessedTime(); long t2=session.getC ...
- struts全包导入问题
web.xml如下: <?xml version="1.0" encoding="UTF-8"?> <web-app xmlns:xsi=&q ...
- ZOJ 3609 Modular Inverse(拓展欧几里得求最小逆元)
Modular Inverse Time Limit: 2 Seconds Memory Limit: 65536 KB The modular modular multiplicative ...
- 【洛谷】P1196 银河英雄传说(并查集)
题目描述 公元五八○一年,地球居民迁至金牛座α第二行星,在那里发表银河联邦创立宣言,同年改元为宇宙历元年,并开始向银河系深处拓展. 宇宙历七九九年,银河系的两大军事集团在巴米利恩星域爆发战争.泰山压顶 ...
- CSS 基本知识梳理-续
CSS 基本知识 1.CSS 简介 CSS 指层叠样式表 (Cascading Style Sheets),是一种用来表现 HTML 文档样式的语言,样式定义如何显示 HTML 元素,是能够真正做到网 ...
- Autofac ASP.NET Web API (Beta) Integration
With the beta release of ASP.NET MVC 4 and the ASP.NET Web API being released a few weeks ago, I dec ...
- 导出ExcelDemo
public String exportExcel(){ String message=null; SimpleDateFormat df =new SimpleDateFormat("yy ...
- Promise题目
setTimeout(function () { console.log(1); }, 0) new Promise(function executor(resolve) { console.log( ...
- Disconf实践指南:安装篇
Disconf是百度开源出来的一款基于Zookeeper的分布式配置管理软件.目前很多公司都在使用,包括滴滴.百度.网易.顺丰等公司.通过简单的界面操作就可以动态修改配置属性,还是很方便的.使用Dis ...
- PHP5 ini配置文件优化
1.1使用tmpfs作为缓存加速缓存的文件目录 [root@php-node1 ~]# mount -t tmpfs tmpfs /dev/shm/ -o size=256m [root@php-no ...