Golang 面向对象全解
golang的面向对象是不完备的面向对象,大部分人员都是从java转换而来,造成了很多的坑,这一篇文章记录我已知的golang面向对象性质和坑,希望后来者注意。
此文仅是个人在学习过程中摸索的自我总结,无法保证完全理解正确,如有错误,欢迎指正,共同学习!
基础
go中没有class的概念,只有与之类似的struct结构体(很像c语言),还有接口interface
接下来简单对比一下,他与普通面向对象语言的概念上的区别。
类/结构 | 接口 | |
go | struct 结构体 定义阶段只能描述成员变量 后面通过在函数前面增加标识符来完成方法的描述,可读性不如java | interface 接口 只能描述方法接口 |
java | class 类 可以描述成员变量、方法 | interface 接口 可以描述成员与方法接口 |
定义上go的interface只用作一种类型定义,可以认为是方法的集合,我们不需要显式的去声明struct继承了什么接口 然后去实现它,而是先去实现那个方法,所有实现了接口中所描述方法的结构体都认为其实现了这个接口。
这个特性非常有趣,但是貌似也容易出错,无法显式地告诉我们,我们哪些方法还未实现。
而还有一个特殊的接口interface{}被叫做空接口,里面有0个方法,认为所有对象都实现了空接口
在做代码对比之前,我们需要先引入接口类型判断的方法,才能说明问题。
类型判断/转换
a:=3
float32(a)
switch interface{}(float32(3)).(type) {
case float32:
fmt.Prntln("this is float32")
}
interface{}(3)//所有对象都实现了空接口
newObject,ok:=Object.(someType)
golang中的接口和结构体
type Person struct {
Name string
}
var p Person=Person{"Jack"}
//Person类定义同上
//方式一
func (p Person) say(){
fmt.Println(p.Name)
}
//这样就定义了一个对象方法,其中p是Person对象的实例
//
//方式二
func (p *Person) say(){
fmt.Println(p.Name)
}
//此时p是Person对象的指针,此时p.Name也可以等价为(*p).Name,go自动做了转换,所以两个代码等价
//看起来两者都可以完成类方法的定义与实现,但是在使用上会有不同,将会在坑点里面讲
Go的面向对象实现
首先面向对象具有三大特性,封装、继承、多态,go均不直接支持这三种特性,但都可以通过别的方式来实现这三种特性。
封装
golang中没有class只有struct结构体,类比c语言。go中成员变量没有权限修饰符,public等,其通过成员变量的变量名开头大小写来控制访问。
type Person struct {
Name string//公有变量
ago int//私有变量
}
继承
go中继承是通过包含来实现的。
包含有两种情况,一种是和java一样,父亲有一个儿子对象,儿子和父亲各自拥有自己的名字,此时的儿子和父亲相当于两个不同的类,只是父亲有一个儿子对象而已。
package main
import "fmt"
type Person struct {
Name string
}
type Say interface {
say()
}
func (p *Person)say() {
fmt.Println("My name is"+p.Name)
}
type Father struct {
son Person
Name string
}
func (f Father)teach() {
fmt.Println("I am "+f.Name+", teaching my son"+f.son.Name)
}
func main() {
p:=Person{"Jack"}
f:=Father{p,"Tom"}
f.teach()
}
上面是java意义下的包含,下面这一种是通过包含实现go的继承,通过传入匿名对象,使得父类的所有成员变量、方法都变为子类的。
package main
import "fmt"
type Person struct {
Name string
}
type Say interface {
say()
}
func (p *Person)say() {
fmt.Println("My name is "+p.Name)
}
type Teacher struct {
Person
}
func (f Teacher)teach() {
fmt.Println("I am a teacher! And My name is "+f.Name)
}
func main() {
t:=Teacher{Person{"Tom"}}
t.say()
t.teach()
}
多态
同一个接口不同实现,这个方法我认为在go中是比较鸡肋的。
package main
import "fmt"
type Person struct {
Name string
}
type Say interface {
say()
}
func (p Person)say() {
fmt.Println("My name is "+p.Name)
}
type Teacher struct {
Name string
}
func (f Teacher)say() {
fmt.Println("I am a teacher! And My name is "+f.Name)
}
func main() {
var t,p Say =Teacher{"Tom"},Person{"Jack"}
t.say()
p.say()
}
坑点
package main
import (
"fmt"
)
type Person struct {
Name string
}
type Say interface {
say()
}
func (p Person)say() {
fmt.Println("My name is "+p.Name)
}
func main(){
//以下两种都可以,但是在大多数情况下,对于接口直接用指针赋值一定不会错
//这两者的不同请看下一个坑点
//var p Say=&Person{"Tom"}
var p Say=Person{"Tom"}
p.say()
}
//上面不变
func (p *Person)say() {
fmt.Println("My name is "+p.Name)
}
func main(){
//这句话报错
//var p Say=Person{"Tom"}
//这句话正常
var p Say=&Person{"Tom"}//new(Person)返回的也是指针,也可以使用
p.say()
}
package main
import (
"fmt"
)
type Person struct {
Name string
}
//type Say interface {
// say()
//}
func (p *Person)call() {
p.say()
}
func (p *Person)say() {
fmt.Println("My name is "+p.Name)
}
type Teacher struct {
Person
}
func (t Teacher)say() {
fmt.Println("I'm a teacher,"+t.Name)
}
func main(){
var t Teacher=Teacher{Person{"Tom"}}
t.call()
}
package main
import (
"fmt"
)
type Person struct {
Name string
}
type Say interface {
say()
}
func call(s Say) {
s.say()
}
func (p *Person)say() {
fmt.Println("My name is "+p.Name)
}
type Teacher struct {
Person
}
func (t Teacher)say() {
fmt.Println("I'm a teacher,"+t.Name)
}
func main(){
var t Teacher=Teacher{Person{"Tom"}}
call(t)
}
package main
import (
"fmt"
)
type Person struct {
Name string
}
type Say interface {
say()
}
//注意此处 前缀只用作限制其方法所属,真正实例由方法调用时传入
func (Person)call(s Say) {
s.say()
}
func (p Person)say() {
fmt.Println("My name is "+p.Name)
}
type Teacher struct {
Person
}
func (t Teacher)say() {
fmt.Println("I'm a teacher,"+t.Name)
}
func main(){
var t Teacher=Teacher{Person{"Tom"}}
t.call(t)
}
golang的坑点大多数都在interface上,这是我们学习常规面向对象语言之后所遗留下来的情况,听说go官方并不是很建议用go去面向对象,但是面向对象确实能提高效率。。。我们在go中不能以之前的面向对象思维去看待它。