接触了go语言已经有几个月的时间了,虽然大部分的系统包还不是很熟练,但是对于go也算是有一定的了解。go语言在语言的层面上并没有非常亮眼的地方。在效率方面,go语言不及c/c++,在移植性上go语言不及java,在高并发方面erlang也能做的很好。所以go会给人有一种很全能的感觉,在各个方面都做了适当的取舍。在开发效率上go会比较快些。
go语言中,interface是重点。在面向对象设计中,有组合优于继承这句话。go语言放弃了继承,在组合方面表现的很好。首先来看看interface的用法。interface有两种用法:
1,空接口,用过C/C++的人看了空接口的定义第一时间想到的就是C/C++中的void*。
var Businesser interface{} var IntData int = 100 var StringData string = "i love hxc" Businesser = IntData var IntData2 int = Businesser.(int) fmt.Println(Businesser) // 100 fmt.Println(IntData2) //100 Businesser = StringData var StringData2 string = Businesser.(string) fmt.Println(StringData2) // "i lobe hxc" fmt.Println(Businesser) // "i love hxc"
从上面的用法上来看,空接口的用法看起来确实和C/C++中的void*一致。但是在C/C++中一个不知道从何处来的void*类型的变量(当然,从程序设计的角度上看,这种情况是要尽量避免的),我们不能判断它原来的类型,在go语言中对于一个空接口却可以通过类型断言,或者借助系统包中的reflect包对还原出原来赋给空接口的数据类型。
func InterfaceUsage(rhs interface{}) { switch val := rhs.(type) { case int: ... case string: ... } }
从上面对空接口的描述可以知道空接口有点类似面向对象语言中的根对象object。然而,在go语言使用interface的时候有一个问题很需要注意。
var Businesser interface{} var IntData int = 100 var StringData string = "i love hxc" Businesser = IntData var IntData2 int = Businesser.(int) IntData = 101 fmt.Println(Businesser) // 100 fmt.Println(IntData2) //100 Businesser = StringData StringData = "hxc love me too" var StringData2 string = Businesser.(string) fmt.Println(StringData2) // "i lobe hxc" fmt.Println(Businesser) // "i love hxc"
从上面代码的输出可以看出在当一个变量赋给一个接口时是将该变量复制一份给空接口。这样就造成了我们看到的变量的内容改变了,空接口保存的变量的内容却不变。要避免这种问题的方法是将变量的地址赋给空接口,这样既保证了两者内容的一致,而且避免了大量的复制工作。该方面的问题在非空接口是一致的。
2,非空接口
go 语言中并没有显式地支持继承。但是也提供了实现继承的方法,而且实现起来也不是很直观。在go语言中建议使用非空接口。非空接口用法如下:
type Personer interface { Walk() ChangeName(string) } type GoodPerson struct { name string } func NewGoodPerson(name string) *GoodPerson { return &GoodPerson{name} } func (this *GoodPerson)Walk() { fmt.Println(this.name, " walking") } func (this *GoodPerson)ChangeName(name string) { this.name = name } func (this *GoodPerson)Fly() { fmt.Println("man can't fly") } func main() { var Person Personer = NewGoodPerson("hxc") var Person2 interface{} = Person Person.Walk() // "hxc walking" Person.ChangeName("wzm") Person.Walk() // "wzm walking" // Person.Fly() // error Personer have no field or mothed name Fly // Person2.Walk() // error //Person = 1 //error }
一个数据类型如果实现了接口的方法,那么该数据类型就可以赋值给非空接口。如果该数据类型没有实现非空接口中的方法,那么也是不能赋值给非空接口的。可空接口一样是赋值个接口,其底层会进行拷贝,所以数据类型实现接口的方法是传递指针的比较好。因为这样调用了非空接口就可以改变赋给该非空接口变量的内容。
在go语言中,一种数据类型无需如其他语言一样显式地说明实现了哪个接口。所以,一种数据类型可以实现多种接口。由于go语言中没有像c++中的构造函数,所以在习惯上go语言都会通过一个工厂产生特定的数据变量。