Go语言学习(十一)面向对象编程-类型系统

1.类型系统介绍

对于面向对象编程的支持Go 语言设计得非常简洁而优雅。简洁之处在于,Go语言并没有沿

袭传统面向对象编程中的诸多概念,比如继承、虚函数、构造函数和析构函数、隐藏的 this 指

针等。

一个典型的类型系统通常包含如下基本内容:

  • 基础类型,如 byte 、 int 、 bool 、 float 等;
  • 复合类型,如数组、结构体、指针等;
  • 可以指向任意对象的类型( Any 类型);
  • 面向对象,即所有具备面向对象特征(比如成员方法)的类型;
  • 接口。

因为Java语言自诞生以来被称为最纯正的面向对象语言,所以我们就先以Java语言为例讲一讲类型系统。

在Java语言中,存在两套完全独立的类型系统:一套是值类型系统,主要是基本类型,如 byte 、

int 、 boolean 、 char 、 double 等,这些类型基于值语义;一套是以 Object 类型为根的对象类型

系统,这些类型可以定义成员变量和成员方法,可以有虚函数,基于引用语义,只允许在堆上创建

(通过使用关键字 new )。Java语言中的 Any 类型就是整个对象类型系统的根—— java.lang.Object

类型,只有对象类型系统中的实例才可以被 Object 类型引用。值类型想要被 Object 类型引用,需要装箱

(boxing)过程,比如 int 类型需要装箱成为 Integer 类型。另外,只有对象类型系统中的类型才可

以实现接口,具体方法是让该类型从要实现的接口继承。

相比之下,Go语言中的大多数类型都是值语义,并且都可以包含对应的操作方法。在需要

的时候,你可以给任何类型(包括内置类型)“增加”新方法。而在实现某个接口时,无需从

该接口继承(事实上,Go语言根本就不支持面向对象思想中的继承语法),只需要实现该接口

要求的所有方法即可。任何类型都可以被 Any 类型引用。 Any类型就是空接口,即interface{} 。

2.为类型添加方法

在Go语言中,你可以给任意类型(包括内置类型,但不包括指针类型)添加相应的方法,

例如:

package main

import(
    "fmt"

)
//定义一个新的类型
type Integer int

func main(){
    //使用自定义的类型定义变量
    var a Integer = 3
    if a.Less(4){
        fmt.Println(a,"less 4")
    }
}
//定义方法,调用者为Integer类型,接收Integer类型,返回bool值
func (a Integer) Less(b Integer)bool{
    return a < b
}

在这个例子中,我们定义了一个新类型 Integer ,它和 int 没有本质不同,只是它为内置的

int 类型增加了个新方法 Less() 。这样实现了Integer后,就可以让整型像一个普通的类一样使用了.

上面的方法的调用方式是 类型.方法名(参数列表),例如:

a.Less(4)

这种调用方式就是面向对象的体现.

他和面向过程有明显的区别,如下修改为面向过程的方法定义.

func Integer_Less(a,b integer)bool{
    return a < b
}

调用方式是 方法名(参数列表),例如:

Integer_Less(a,2)

3.通过指针修改对象

Go语言中的面向对象最为直观,也无需支付额外的成本。如果要求对象必须以指针传递,

这有时会是个额外成本,因为对象有时很小(比如4字节),用指针传递并不划算。

只有在你需要修改对象的时候,才必须用指针。它不是Go语言的约束,而是一种自然约束。

举个例子:

package main

import(
    "fmt"
)
type Integer int

func main(){
    var a Integer = 3
    a.Add(3)
    fmt.Println("a=",a)//a=6
}
//通过Integer的指针调用
func (a *Integer) Add(b Integer){
     *a += b //修改变量a的指针对应的值
}

运行该程序,得到的结果是: a=6 。

如果你实现成员方法时的调用者不是a的指针而是值(即传入Integer,而非 *Integer)

如下所示:

func (a Integer) Add(b Integer) {
    a += b
}

那么运行程序得到的结果是a=3

也就是维持原来的值,这点需要特别注意.

究其原因,是因为Go语言和C语言一样,类型都是基于值传递的。要想修改变量的值,只能

传递指针。

4.值语义和引用语义;

值语义和引用语义的差别在于赋值

如果 b 的修改不会影响 a 的值,那么此类型属于值类型。如果会影响 a 的值,

那么此类型是引用类型.

Go语言中的大多数类型都基于值语义,包括:

  • 基本类型,如 byte 、 int 、 bool 、 float32 、 float64 和 string 等;
  • 复合类型,如数组(array)、结构体(struct)和指针(pointer)等。

Go语言中的数组和基本类型没有什么区别,是很纯粹的值类型,例如:

var a = [3]int{1,2,3}
var b = a
b[1]++
fmt.Println(a, b)

该程序的运行结果如下:

[1 2 3] [1 3 3]。

这表明 b=a 赋值语句是数组内容的完整复制。要想表达引用,需要用指针:

var a = [3]int{1, 2, 3}
var b = &a
b[1]++
fmt.Println(a, *b)

该程序的运行结果如下:

[1 3 3] [1 3 3]

这表明 b=&a 赋值语句是数组内容的引用。变量 b 的类型不是 [3]int ,而是 *[3]int 类型。

Go语言中有4个类型比较特别,看起来像引用类型,如下所示。

1.数组切片:指向数组(array)的一个区间。

2.map:极其常见的数据结构,提供键值查询能力。

3.channel:执行体(goroutine)间的通信设施。

4.接口(interface):对一组满足某个契约的类型的抽象。

但是这并不影响我们将Go语言类型看做值语义。

数组切片本质上是一个区间,你可以大致将 []T 表示为:

type slice struct {
    first *T
    len int
    cap int
}

因为数组切片内部是指向数组的指针,所以可以改变所指向的数组元素并不奇怪。数组切片

类型本身的赋值仍然是值语义。

5.结构体

Go语言的结构体(struct)和其他语言的类(class)有同等的地位,但Go语言放弃了包括继

承在内的大量面向对象特性,只保留了组合(composition)这个最基础的特性。

组合甚至不能算面向对象特性,因为在C语言这样的过程式编程语言中,也有结构体,也有

组合。组合只是形成复合类型的基础。

上面我们说到,所有的Go语言类型(指针类型除外)都可以有自己的方法。在这个背景下,

Go语言的结构体只是很普通的复合类型,平淡无奇。例如,我们要定义一个矩形类型:

type Rect struct {
    x, y float64
    width, height float64
}
//然后我们定义成员方法 Area() 来计算矩形的面积:
func (r *Rect) Area() float64 {
    return r.width * r.height
}

可以看出Go语言中结构体的使用方式与C语言并没有明显不同。

时间: 2024-07-28 21:23:28

Go语言学习(十一)面向对象编程-类型系统的相关文章

Go语言学习(十三)面向对象编程-继承

1.匿名组合 Go语言也提供了继承,但是采用了组合的方式,所以我们将其称为匿名组合: package main import "fmt" //定义基类 type Base struct { Name string } //基类相关的2个成员方法 func (base *Base) A() { fmt.Println("Base method A called...") } func (base *Base) B() { fmt.Println("Base

如何在C语言里实现“面向对象编程”

有人认为面向对象是C++/Java这种高级语言的专利,实际不是这样,面向对象作为一种设计方法,是不限制语言的.只能说,用C++/Java这种语法来实现面向对象会更容易.更自然一些. 在本节中,就展示如何在C语言中实现面向对象编程,这是一件吃力的工作.写这些的目的有两个: ① 更好的掌握C++中的class的概念.学习了本章,就知道C程序员的无耐,就知道为什么要发明一个class的概念.为什么要有成员函数等等. ② 为C程序员提供一个参考设计.由在存在某些场合,只允许用C语言来编程,不允许用C++

Python学习笔记——面向对象编程

接下来学习面向对象编程,基础的就不记录了,只记录一些Python特有的或者高级的特性. http://www.liaoxuefeng.com/wiki/0014316089557264a6b348958f449949df42a6d3a2e542c000/0014318645694388f1f10473d7f416e9291616be8367ab5000 1. 类的定义 定义类使用class关键字,后面紧跟类名(首字母大写),接着是从哪个类继承下来的(所有类最终会继承object). 通过类名加参

从0开始的Python学习014面向对象编程

 简介 到目前为止,我们的编程都是根据数据的函数和语句块来设计的,面向过程的编程.还有一种我们将数据和功能结合起来使用对象的形式,使用它里面的数据和方法这种方法叫做面向对象的编程. 类和对象是面向对象编程的两个重要方面.对于类和对象的关系,举个例子就像学生和小明同学的关系一样.学生(类)是一个拥有共同属性的群体,小明同学(对象)是其中一个有自己特性的个体. 对于一个对象或类的变量被称为域,函数被称为类或对象的方法. 域有两种类型--属于每个对象或属于类本身,分别成为实例变量和类变量. 类使用cl

开始学习Python面向对象编程

之前粗浅地学习过C++,对于面向对象有些了解,现在通过Python仔细学习一下面向对象: 类 类使用 class 关键字创建.类的域和方法被列在一个缩进块中. class Person: pass #pass语句表示一个空类 p = Person() print(p) $ python simplestclass.py <__main__.Person object at 0x019F85F0>#我们已经在 __main__ 模块中有了一个 Person 类的实例 对象的方法 class Pe

Python学习之==&gt;面向对象编程

一.面向对象与面向过程 面向对象与面向过程是两种不同的编程范式,范式指的是按照什么方式去编程.去实现一个功能.不同的编程范式本质上代表对各种不同类型的任务采取不同的解决问题的思路. 1.面向过程编程 角色是执行者,把一个项目按照一定的顺序,从头到尾一步步执行下去.这种思想好理解,但只要前面一个步骤变了,后面的步骤也要跟着变,维护起来比较麻烦. 2.面向对象编程 角色是指挥者,把一个项目分成一个个小的部分,每个部分负责一方面的功能,整个项目由这些部分组合而成一个整体.类似一个机关,分为各个职能部门

cg语言学习&amp;&amp;阳春白雪GPU编程入门学习

虽然所知甚少,但康大的<GPU编程与Cg编程之阳春白雪下里巴人>确实带我入了shader的门,在里面我第一次清晰地知道了"语义"的意思,非常感谢. 入门shader,我觉得可以先读3本书:<GPU编程与Cg编程之阳春白雪下里巴人>=><cg教程>=><Real-Time Rendering 3rd>(在读,最近忙,搁下了),打下理论基础. 下面是<cg教程>的读书笔记. 1.基本cg函数 1)数学函数:abs,ac

python学习day11 面向对象编程 类和实例

class Student(object): #class后面紧接着是类名,即Student,类名通常是大写开头的单词,紧接着是(object),表示该类是从哪个类继承下来的.通常,如果没有合适的继承类,就使用object类,这是所有类最终都会继承的类. def __init__(self,name,score): #通过定义一个特殊的__init__方法,在创建实例的时候,就把类的name,score等属性绑上去,__init__方法的第一个参数永远是self,表示创建的实例本身,因此,在__

python学习日记-面向对象编程(一)

python的类(class)和实例(instance) 假设一种鞋子(shoe)有尺码(size)和颜色(color)两种属性,以此为例. 类可以起到模板的作用,因此,可以在创建实例的时候,把一些我们认为必须绑定的属性强制填写进去.通过定义一个特殊的__init__方法,在创建实例的时候,就把size,color属性绑到shoe上去,例如: 1 class Shoe(object): 2 3 def __int__(self, size, color): 4 self.size = size