類型系統(tǒng)
類型系統(tǒng)是指一個語言的類型體系結(jié)構燕侠。一個典型的類型系統(tǒng)通常包含以下內(nèi)容:
- 基礎類型:byte int bool ...
- 復合類型:數(shù)組 結(jié)構體 指針 ...
- 可以指向任意對象的類型(Any類型)
- 值語義和引用語義
- 面向?qū)ο蟮奶卣?如成員方法
- 接口
類型系統(tǒng)描述的是這些內(nèi)容在一個語言中如何被關聯(lián)诵棵。
以java舉例,在Java中存在兩套完全獨立的類型系統(tǒng):一套是值類型系統(tǒng),主要是基本數(shù)據(jù)類型(int byte char 等 )状原,這些類型是值語義的挚赊。另一套則是以Object為根的對象類型,這些類型可以定義成員變量和成員方法洪橘。java中的Any類型就是Object,值類型想要被Object引用需要裝箱(int->Integer)跪者。
相比之下Go語言的大多數(shù)類型都是值語義,并且都可以包含對應的操作方法熄求。在需要的時候可以給任何類型添加新方法渣玲。而在實現(xiàn)某個接口時,無需從該接口繼續(xù)(Go不支持繼承)弟晚,只要實現(xiàn)該接口要求的所有方法即可忘衍。GO語言的Any類型就是空接口interface()逾苫,可以引用任何類型。
為類型添加方法
例1:
package main
import "fmt"
type Integer int
func (a Integer) Less(b Integer) bool {
return a<b
}
func main() {
var a Integer =1
if a.Less(2){
fmt.Println(a,"less 2")
}
}
運行結(jié)果:
1 less 2
Process finished with exit code 0
例1 定義了一個新類型Integer,我們?yōu)槠湫略隽艘粋€Less方法枚钓。
例2:
package main
import "fmt"
type Integer int
func (a *Integer) Add(b Integer) {
*a += b
}
func main() {
var a Integer =1
a.Add(2)
fmt.Println(a)
}
Go語言中的面向?qū)ο笞顬橹庇^铅搓,如果要求對象必須以指針傳遞,這有時會是個額外成本搀捷,有時對象很小星掰,用指針傳遞并不劃算。
只有在你需要修改對象的時候嫩舟,才必須使用指針氢烘,比如例2
如果你寫成這樣
func (a *nteger) Add(b Integer) {
a += b
}
那么你得打的值仍然會是1,而不是3家厌。因為go語言類型都是屬于值傳遞播玖,要想修改變量的值,只能傳遞指針像街。
值語義和引用語義
值語義和引用語義的差別在于賦值
b = a
b.Modify()
如果b的修改不會影響a,屬于值類型黎棠,如果影響屬于引用類型。
Go語言中大多數(shù)屬于值類型镰绎,包括:
- 基本類型 int bool 等
- 復合類型 array 結(jié)構體struct 指針
Go語言中有4個類型比較特別脓斩,看起來像引用類型
- 數(shù)組切片:指向數(shù)組的一個區(qū)間
- map:極其常見的數(shù)據(jù)類型
- channel:執(zhí)行體間的通信設施
- 接口:對一組滿足某個契約的類型的抽象
結(jié)構體
Go 語言的結(jié)構體(struct)和其他語言的類(class)有同等地位,但Go放棄了包括繼承在內(nèi)的大量的面向?qū)ο蟮奶匦猿肫埽槐A袅私M合這個最基礎的特性随静。
定義一個矩形類型,再定義Area成員方法來計算面積
type Rect struct {
x, y float64
width, hight float64
}
func (rect *Rect)Area() float64 {
return rect.hight*rect.width
}
初始化
定義好了Rect吗讶,可以有以下幾種方式初始化:
rect1 := new(Rect)
rect2 :=&Rect{0,0,100,100}
rect3 :=&Rect{width:100,hight:100}
rect4 :=&Rect{}
Go語言沒有構函數(shù)燎猛,使用一個全局的創(chuàng)建函數(shù)new來創(chuàng)建
匿名組合
確切的說,Go語言也提供了繼承照皆,但是采用了組合的文法重绷,我們稱之為匿名組合
type Base struct {
Name string
}
func (base *Base)Foo() {
}
func (base *Base)Bar() {
}
type Foo struct {
Base
}
func (foo *Foo) Bar() {
foo.Base.Bar()
}
以上代碼定義了一個Base類,實現(xiàn)了Foo()和Bar(),然后定義了一個Foo類膜毁,該類從Base類“繼承”并改寫了Bar()
可見性
Go語言對關鍵字的增加非常的吝嗇昭卓,其中沒有private、protected瘟滨、public這樣的關鍵字候醒,要使某個符號對其他包可見,需要定義此符號定義為大寫字母
接口
接口在Go語言有著至高重要的地位杂瘸,接口是Go語言整個類型系統(tǒng)的基石倒淫,讓Go語言在基礎編程哲學的探索上達到前所未有的高度。
非侵入式接口
在Go語言中败玉,一個類只需要實現(xiàn)了接口所有的函數(shù)敌土,我們就說這個類實現(xiàn)了該接口镜硕,甚至可以不知道接口的存在,例如以下File類并沒有繼承IFile IRead IWrite返干,但是實現(xiàn)了這些接口谦疾,所以就可以進行賦值。
type IFile interface{
Read (buf []byte) (n int,err error)
Write(buf []byte)(n int,err error)
Seek(off int64,whene int)(pos int64,err error)
Close(err error)
}
type IRead interface{
Read (buf []byte) (n int,err error)
}
type IWrite interface{
Write (buf []byte) (n int,err error)
}
type File struct {
}
func (f *File) Read (buf []byte) (n int,err error){}
func (f *File) Write(buf []byte)(n int,err error){}
func (f *File) Seek(off int64,whene int)(pos int64,err error){}
func (f *File) Close(err error){}
//賦值
var file1 IFile = new(File)
var file2 IRead = new(File)
var file3 IWrite = new(File)
接口賦值
接口賦值在Go語言中有兩種情況:
將對象實例賦值給接口
將一個接口賦值給另一個接口
將某種類型的對象賦值給接口
type Integer int
func (a Integer) Less (b Integer)bool{
return a<b
}
func (a *Integer) Add (b Integer)bool{
*a +=b
}
//定義接口LessAdder
type LessAdder interface{
Less (b Integer)bool
Add (b Integer)
}
//將對象賦值給接口
var a Integer = 1
var b LessAdder =&a (1)
var b LessAdder =a (2)
//其中(1)是正確的賦值是(1)