golang中的类型系统

时间:2022-04-04 15:46:08

序:

在Java语言中,存在两套完全独立的类型系统:一套是值类型系统,主要是基本类型,如byte int boolean char double等,这些类型基于值语义;一套是以object类型为根的对象类型系统,这些类型可以定义成员变量和成员方法,可以有虚函数,基于引用语义,只允许在堆上创建(通过使用关键字new)。Java语言中的Any类型就是整个对象系统的根――java.lang.Object类型,只有对象类型系统中的实例才可以被Any类型引用。值类型想要被Any类型引用,需要装箱(boxing)过程,比如int类型需要装箱成为Integer类型。另外,只有对象类型系统中的类型才可以实现接口,具体方法是让该类型从要实现的接口继承。

相比之下,Go语言中的大多数类型都是值语义,并且都可以包含对应的操作方法。在需要的时候,你可以给任何类型(包括内置类型)"增加"新方法。而在实现某个接口时,无需从该接口集成(事实上,GO语言根本就不支持面向对象思想中的继承语法),只需要实现该接口要求的所有方法即可。任何类型都可以被Any类型引用。Any类型就是空接口,即interface()。


为类型添加方法:

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

type Integer int

func (a Integer) Less (b Integer) bool {

    return a < b

}

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

这样实现了Integer后,就可以让整型像一个普通类一样使用:

func main() {

    var a Integer = 1

    if a.Less(2) {

        fmt.Println(a, "Less 2")

    }

}


值语义和引用语义:

值语义和引用语义的差别在于赋值,比如下面的例子:

b = a

b.Modify()

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

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

基本类型,如byte int bool float32 float64 和 string 等

复合类型,如数组(array) 结构体(struct) 和指针(pointer) 等

GO语言中类型的值语义表现的非常彻底。之所以这么说,是因为数组。

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个类型比较特别,看起来像引用类型,如下所示。

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

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

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

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

但是这并不影响我们将GO语言类型看做值语义。下面我们来看看这4个类型。

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

type slice struct {

    first *T

    len int

    cap int

}

因为数组切片是指向数组的指针,所以可以改变所指向的数组元素并不奇怪。数组切片类型本身的赋值仍然是值语义。


结构体:

GO语言的结构体(struct)和其他语言的类(class)有同等的地位,但Go语言放弃了包括集成在内的大量面向对象特性,只保留了组合(composition)这个最基础的特性。

上面我们说到,所有的Go语言类型(指针类型除外)都可以有自己的方法。在这个背景下,Go语言的结构体只是很普通的复合类型,平淡无奇。例如,我们要定义一个矩形类型:

type Rect struct {

    x, y float64

    width, height float64

}

然后我们定义成员方法Area()来计算矩形的面积:

func (r *Rect) Area() float64 {

    return r.width * r.height

}