Go 語言的大多數(shù)類型都是值語義,并且都可以包含對(duì)應(yīng)的操作方法。在需要的時(shí)候,你可以給內(nèi)置類型“增加”新方法。兒在實(shí)現(xiàn)某個(gè)具體的接口時(shí),無需從該接口繼承,只需要實(shí)現(xiàn)該接口要求的所有方法即可。任何類型都可以被Any 類型引用。Any 類型就是空接口,即interface{}。
type Integer int
func (a Integer) Less(b Integer) bool {
return a < b
}
func main() {
fmt.Println("Hello")
var a, b Integer = 1,2
fmt.Println(a,b)
res := a.Less(b)
fmt.Println(res)
}
上面給 Integer 添加了一個(gè) Less 方法第一個(gè) a 是給 a 添加方法,b 是參數(shù),bool 是函數(shù)返回值。
在 Go 語言中,面向?qū)ο蟮纳衩孛婕啿粡?fù)存在,不像 C++ 和 Java,方法都默認(rèn)有一個(gè)隱藏的 this 指針,Python 中方法中的 self 和 this 指針的作用是一樣的。Go 中方法施加的目標(biāo)(對(duì)象),顯示傳遞;不需要非得是指針,也不用非得叫this。只有在需要修改對(duì)象的時(shí)候才有必要使用指針。比如:
func (a *Integer) Add(b Integer) {
*a += b
}
Go 語言和 C 語言是一樣的,都是基于值傳遞的。要想修改變量的值,只能傳遞指針。
Go 語言大多數(shù)類型都基于值語義:基本類型+符合類型(數(shù)組、結(jié)構(gòu)體、指針)。和 C 語言不同的,數(shù)組的傳遞也是基于值語義的,而且很純粹。比如:
var arrayA [3]int = [3]int{1,2,3}
arrayB := arrayA
arrayB[1]++
fmt.Println(arrayA,arrayB)
會(huì)看到輸出,對(duì) B 中元素的操作,對(duì) A 并沒有任何影響。這表明在將 A 賦值給 B 的時(shí)候是數(shù)組內(nèi)容的完整復(fù)制,如果想要表達(dá)引用,需要使用指針: arrayB := &arrayA
數(shù)組切片、map、channel、接口看起來想引用類型
Go 中的結(jié)構(gòu)體和其他語言的類有相同的地位,但是 Go 語言中沒有繼承在內(nèi)的大量面向?qū)ο蟮奶匦裕槐A袅私M合(composition)這個(gè)最基礎(chǔ)的特性。比如下面的例子對(duì) log 的使用
type Job struct {
Command string
*log.Logger
}
func NewJob(command string, logger *log.Logger) *Job {
return &Job{command,logger}
}
func (job *Job)Start(){
job.Println("Starting now....")
job.Println("end")
}
func main() {
mylog := log.New(os.Stdout, "",log.LstdFlags)
j := NewJob("test",mylog)
j.Start()
}
定義的 Job 結(jié)構(gòu)體包含一個(gè)匿名的 log.Logger 指針,在合適的賦值之后可以在 Job 類型的所有成員方法中可以很舒適的借用所有 log.Logger 提供的方法。類似于 Java 和 C++ 中繼承了 log.Logger
Go 語言中并沒有其他語言中的 public 等權(quán)限訪問控制的關(guān)鍵字。要使某個(gè)符號(hào)對(duì)其他包可見,需要將該符號(hào)定義為以大寫字母開頭,就想上面定義的 Job 結(jié)構(gòu)體首字母需要大寫。成員方法的訪問性遵循同樣的規(guī)則,如果上面的 Start 函數(shù)定義為 start,則該函數(shù)只能在該類型所在的包內(nèi)使用。
Go 語言中的接口不需要像 C++ 和 Java 中的使用:或者 implements 關(guān)鍵字來實(shí)現(xiàn)接口,在 Go 語言中,一個(gè)類只需要實(shí)現(xiàn)了接口要求的所有函數(shù),那么這個(gè)類就實(shí)現(xiàn)了該接口。下面的例子:
//定義一個(gè)文件接口
type IFile interface {
Read(buf []byte) (n int, err error)
Write(buf []byte) (n int, err error)
Close() error
}
type IReader interface {
Read(buf []byte) (n int, err error)
}
type IWriter interface {
Write(buf []byte) (n int, err error)
}
type ICloser interface {
Close() error
}
//定義文件類
type File struct {
filename string
fid int16
}
//定義文件的幾個(gè)方法
func (f *File)Read(buf []byte) (n int, err error){
fmt.Println("File Opened")
return 0,nil
}
func (f *File)Write(buf []byte) (n int,err error) {
fmt.Println("File Writed")
return 0,nil
}
func (f *File)Close() error {
fmt.Println("File Closed")
return nil
}
func main() {
bytes := []byte{0,1,2,3,4,5,6,7,8,9}
var file1 IFile = new(File)
file1.Read(bytes)
var file2 IReader = new(File)
file2.Read(bytes)
var file3 IWriter = new(File)
file3.Write(bytes)
var file4 ICloser = new(File)
file4.Close()
}
File 類實(shí)現(xiàn)了上面的所有接口,所以可以直接進(jìn)行賦值。Go 語言的接口是非侵入式接口:
接口還可以通過組合來實(shí)現(xiàn):
type IReaderWriter interface {
IReader
IWriter
}
和下面的實(shí)現(xiàn)是一樣的:
type IReaderWriter interface {
Read(buf []byte) (n int, err error)
Write(buf []byte) (n int, err error)
}
Java 語言中所有的對(duì)象都繼承自 Object,而 Go 語言中的任何對(duì)象的實(shí)例都滿足空接口 interface{},所以 interface{}看起來就想指向任何對(duì)象的 Any 類型。
var v1 interface{} = 1
var v2 interface{} = "abc"
var v3 interface{} = &v2
var v4 interface{} = struct { x int }{1}
有過 C++ 語言學(xué)習(xí)經(jīng)歷的朋友都知道,面向?qū)ο笾饕巳齻€(gè)基本特征:封裝、繼承和多態(tài)。封裝,就是指運(yùn)行的數(shù)據(jù)和函數(shù)綁定在一起,C++ 中主要是通過 this 指針來完成的;繼承,就是指 class 之間可以相互繼承屬性和函數(shù);多態(tài),主要就是用統(tǒng)一的接口來處理通用的邏輯,每個(gè) class 只需要按照接口實(shí)現(xiàn)自己的回調(diào)函數(shù)就可以了。
作為集大成者的 Go 語言,自然不會(huì)在面向?qū)ο笊厦鏌o所作為。相比較 C++、java、C# 等面向?qū)ο笳Z言而言,它的面向?qū)ο蟾?jiǎn)單,也更容易理解。下面,我們不妨用三個(gè)簡(jiǎn)單的例子來說明一下 go 語言下的面向?qū)ο笫鞘裁礃拥摹?/p>
package main
import "fmt"
type data struct {
val int
}
func (p_data* data)set(num int) {
p_data.val = num
}
func (p_data* data)show() {
fmt.Println(p_data.val)
}
func main() {
p_data := &data{4}
p_data.set(5)
p_data.show()
}
package main
import "fmt"
type parent struct {
val int
}
type child struct {
parent
num int
}
func main() {
var c child
c = child{parent{1}, 2}
fmt.Println(c.num)
fmt.Println(c.val)
}
package main
import "fmt"
type act interface {
write()
}
type xiaoming struct {
}
type xiaofang struct {
}
func (xm *xiaoming) write() {
fmt.Println("xiaoming write")
}
func (xf *xiaofang) write() {
fmt.Println("xiaofang write")
}
func main() {
var w act;
xm := xiaoming{}
xf := xiaofang{}
w = &xm
w.write()
w = &xf
w.write()
}