1. 程式人生 > >Golang核心程式設計(5)-面向物件之型別系統

Golang核心程式設計(5)-面向物件之型別系統

對於面向物件程式設計的支援Go 語言設計得非常簡潔而優雅。簡潔之處在於,Go語言並沒有沿
襲傳統面向物件程式設計中的諸多概念,比如繼承、虛擬函式、建構函式和解構函式、隱藏的 this 指
針等。優雅之處在於,Go語言對面向物件程式設計的支援是語言型別系統中的天然組成部分。整個
型別系統通過介面串聯,渾然一體。

一、什麼是型別系統

1.1、Go語言的型別系統

顧名思義,型別系統是指一個語言的型別體系結構。一個典型的型別系統通常包含如下基本內容:

  • 基礎型別,如 byte 、 int 、 bool 、 float 等;
  • 基礎型別,如 byte 、 int 、 bool 、 float 等;
  • 複合型別,如陣列、結構體、指標等;
  • 可以指向任意物件的型別( Any 型別);
  • 值語義和引用語義;
  • 面向物件,即所有具備面向物件特徵(比如成員方法)的型別;
  • 介面。

1.2、Java語言的型別系統

型別系統描述的是這些內容在一個語言中如何被關聯。因為Java語言自誕生以來被稱為最純正的面嚮物件語言,所以我們就先以Java語言為例講一講型別系統。

在Java語言中,存在兩套完全獨立的型別系統:一套是值型別系統,主要是基本型別,如 byte 、
int 、 boolean 、 char 、 double 等,這些型別基於值語義;一套是以 Object 型別為根的物件型別系統,這些型別可以定義成員變數和成員方法,可以有虛擬函式,基於引用語義,只允許在堆上建立(通過使用關鍵字 new )。Java語言中的 Any 型別就是整個物件型別系統的根—— java.lang.Object型別,只有物件型別系統中的例項才可以被 Any 型別引用。

二、為型別新增方法

Go語言中的大多數型別都是值語義,並且都可以包含對應的操作方法。在需要
的時候,你可以給任何型別(包括內建型別)“增加”新方法。而在實現某個介面時,無需從該介面繼承(事實上,Go語言根本就不支援面向物件思想中的繼承語法),只需要實現該介面要求的所有方法即可
。任何型別都可以被 Any 型別引用。 Any 型別就是空介面,即 interface{} 。

2.1、為基本型別新增方法

在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")
}
}

2.2、傳遞指標引數和值引數的區別

Go語言中的面向物件最為直觀,也無需支付額外的成本。如果要求物件必須以指標傳遞,這有時會是個額外成本,因為物件有時很小(比如4位元組),用指標傳遞並不划算。

只有在你需要修改物件的時候,才必須用指標。它不是Go語言的約束,而是一種自然約束。

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

這裡為 Integer 型別增加了 Add() 方法。由於 Add() 方法需要修改物件的值,所以需要用指標引用。 呼叫如下:

func main() {
   var a Integer = 1
   a.Add(2)
   fmt.Println("a =", a)
}

執行該程式,得到的結果是: a=3 。如果你實現成員方法時傳入的不是指標而是值(即傳入Integer ,而非 *Integer ),如下所示:

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

那麼執行程式得到的結果是 a=1 ,也就是維持原來的值。

究其原因,是因為Go語言和C語言一樣,型別都是基於值傳遞的。要想修改變數的值,只能傳遞指標。

三、結構體

Go語言的結構體(struct)和其他語言的類(class)有同等的地位,但Go語言放棄了包括繼承在內的大量面向物件特性,只保留了組合(composition)這個最基礎的特性。

組合甚至不能算面向物件特性,因為在C語言這樣的程序式程式設計語言中,也有結構體,也有組合。組合只是形成複合型別的基礎。

3.1、結構體的定義

type Rect struct {
	x, y float64
	width, height float64
}

3.2、結構體的初始化

	rect1 := new(Rect)
	rect2 := &Rect{}
	rect3 := &Rect{0, 0, 100, 200}
	rect4 := &Rect{width: 100, height: 200}

在Go語言中,未進行顯式初始化的變數都會被初始化為該型別的零值,例如 bool 型別的零值為 false , int 型別的零值為0, string 型別的零值為空字串。

在Go語言中沒有建構函式的概念,物件的建立通常交由一個全域性的建立函式來完成,以NewXXX 來命名,表示“建構函式”:

func NewRect(x, y, width, height float64) *Rect {
	return &Rect{x, y, width, height}
}

3.3、可見性

Go語言對關鍵字的增加非常吝嗇,其中沒有 private 、 protected 、 public 這樣的關鍵
字。要使某個符號對其他包(package)可見(即可以訪問),需要將該符號定義為以大寫字母開頭,如:

type Rect struct {
	X, Y float64
	Width, Height float64
}

這樣, Rect 型別的成員變數就全部被匯出了,可以被所有其他引用了 Rect 所在包的程式碼訪問到。