Go语言没有沿袭传统面向对象编程中的诸多概念,好比继承、虚函数、构造函数和析构函数、隐藏的this指针等。编程
Go 语言中同时有函数和方法。方法就是一个包含了接受者(receiver)的函数,receiver能够是内置类型或者结构体类型的一个值或者是一个指针。全部给定类型的方法属于该类型的方法集。数组
以下面的这个例子,定义了一个新类型Integer,它和int同样,只是为它内置的int类型增长了个新方法Less()app
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("less then 2") } }
能够看出,Go语言在自定义类型的对象中没有C++/Java那种隐藏的this指针,而是在定义成员方法时显式声明了其所属的对象。less
method的语法以下:函数
func (r ReceiverType) funcName(parameters) (results)
当调用method时,会将receiver做为函数的第一个参数:测试
funcName(r, parameters);
因此,receiver是值类型仍是指针类型要看method的做用。若是要修改对象的值,就须要传递对象的指针。this
指针做为Receiver会对实例对象的内容发生操做,而普通类型做为Receiver仅仅是以副本做为操做对象,并不对原实例对象发生操做。spa
func (a *Ingeger) Add(b Integer) { *a += b } func main() { var a Integer = 1 a.Add(3) fmt.Println("a =", a) // a = 4 }
若是Add方法不使用指针,则a返回的结果不变,这是由于Go语言函数的参数也是基于值传递。指针
注意:当方法的接受者是指针时,即便用值类型调用那么方法内部也是对指针的操做。code
以前说过,Go语言没有构造函数的概念,一般使用一个全局函数来完成。例如:
func NewRect(x, y, width, height float64) *Rect { return &Rect{x, y, width, height} } func main() { rect1 := NewRect(1,2,10,20) fmt.Println(rect1.width) }
Go语言提供了继承,可是采用了组合的语法,咱们将其称为匿名组合,例如:
type Base struct { name string } func (base *Base) Set(myname string) { base.name = myname } func (base *Base) Get() string { return base.name } type Derived struct { Base age int } func (derived *Derived) Get() (nm string, ag int) { return derived.name, derived.age } func main() { b := &Derived{} b.Set("sina") fmt.Println(b.Get()) }
例子中,在Base类型定义了get()和set()两个方法,而Derived类型继承了Base类,并改写了Get()方法,在Derived对象调用Set()方法,会加载基类对应的方法;而调用Get()方法时,加载派生类改写的方法。
组合的类型和被组合的类型包含同名成员时, 会不会有问题呢?能够参考下面的例子:
type Base struct { name string age int } func (base *Base) Set(myname string, myage int) { base.name = myname base.age = myage } type Derived struct { Base name string } func main() { b := &Derived{} b.Set("sina", 30) fmt.Println("b.name =",b.name, "\tb.Base.name =", b.Base.name) fmt.Println("b.age =",b.age, "\tb.Base.age =", b.Base.age) }
值语义和引用语义的差异在于赋值,好比
b = a b.Modify()
若是b的修改不会影响a的值,那么此类型属于值类型;若是会影响a的值,那么此类型是引用类型。
Go语言中的大多数类型都基于值语义,包括:
C语言中的数组比较特别,经过函数传递一个数组的时候基于引用语义,可是在结构体定义数组变量的时候基于值语义。而在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]
Interface 是一组抽象方法(未具体实现的方法/仅包含方法名参数返回值的方法)的集合,若是实现了 interface 中的全部方法,即该类/对象就实现了该接口。
Interface 的声明格式:
type interfaceName interface { //方法列表 }
Interface 能够被任意对象实现,一个类型/对象也能够实现多个 interface;
interface的变量能够持有任意实现该interface类型的对象。
以下面的例子:
package main import "fmt" type Human struct { name string age int phone string } type Student struct { Human //匿名字段 school string loan float32 } type Employee struct { Human //匿名字段 company string money float32 } //Human实现SayHi方法 func (h Human) SayHi() { fmt.Printf("Hi, I am %s you can call me on %s\n", h.name, h.phone) } //Human实现Sing方法 func (h Human) Sing(lyrics string) { fmt.Println("La la la la...", lyrics) } //Employee重载Human的SayHi方法 func (e Employee) SayHi() { fmt.Printf("Hi, I am %s, I work at %s. Call me on %s\n", e.name, e.company, e.phone) } // Interface Men被Human,Student和Employee实现 // 由于这三个类型都实现了这两个方法 type Men interface { SayHi() Sing(lyrics string) } func main() { mike := Student{Human{"Mike", 25, "222-222-XXX"}, "MIT", 0.00} paul := Student{Human{"Paul", 26, "111-222-XXX"}, "Harvard", 100} sam := Employee{Human{"Sam", 36, "444-222-XXX"}, "Golang Inc.", 1000} tom := Employee{Human{"Tom", 37, "222-444-XXX"}, "Things Ltd.", 5000} //定义Men类型的变量i var i Men //i能存储Student i = mike fmt.Println("This is Mike, a Student:") i.SayHi() i.Sing("November rain") //i也能存储Employee i = tom fmt.Println("This is tom, an Employee:") i.SayHi() i.Sing("Born to be wild") //定义了slice Men fmt.Println("Let's use a slice of Men and see what happens") x := make([]Men, 3) //这三个都是不一样类型的元素,可是他们实现了interface同一个接口 x[0], x[1], x[2] = paul, sam, mike for _, value := range x{ value.SayHi() } }
空interface(interface{})不包含任何的method,正由于如此,全部的类型都实现了空interface。空interface对于描述起不到任何的做用(由于它不包含任何的method),可是空interface在咱们须要存储任意类型的数值的时候至关有用,由于它能够存储任意类型的数值。它有点相似于C语言的void*类型。
// 定义a为空接口 var a interface{} var i int = 5 s := "Hello world" // a能够存储任意类型的数值 a = i a = s
interface的变量里面能够存储任意类型的数值(该类型实现了interface),那么咱们怎么反向知道这个interface变量里面实际保存了的是哪一个类型的对象呢?目前经常使用的有两种方法:switch测试、Comma-ok断言。
switch测试以下:
type Element interface{} type List [] Element type Person struct { name string age int } //打印 func (p Person) String() string { return "(name: " + p.name + " - age: "+strconv.Itoa(p.age)+ " years)" } func main() { list := make(List, 3) list[0] = 1 //an int list[1] = "Hello" //a string list[2] = Person{"Dennis", 70} for index, element := range list{ switch value := element.(type) { case int: fmt.Printf("list[%d] is an int and its value is %d\n", index, value) case string: fmt.Printf("list[%d] is a string and its value is %s\n", index, value) case Person: fmt.Printf("list[%d] is a Person and its value is %s\n", index, value) default: fmt.Println("list[%d] is of a different type", index) } } }
若是使用Comma-ok断言的话:
func main() { list := make(List, 3) list[0] = 1 // an int list[1] = "Hello" // a string list[2] = Person{"Dennis", 70} for index, element := range list { if value, ok := element.(int); ok { fmt.Printf("list[%d] is an int and its value is %d\n", index, value) } else if value, ok := element.(string); ok { fmt.Printf("list[%d] is a string and its value is %s\n", index, value) } else if value, ok := element.(Person); ok { fmt.Printf("list[%d] is a Person and its value is %s\n", index, value) } else { fmt.Printf("list[%d] is of a different type\n", index) } } }
正如struct类型能够包含一个匿名字段,interface也能够嵌套另一个接口。
若是一个interface1做为interface2的一个嵌入字段,那么interface2隐式的包含了interface1里面的method。