Golang 面向对象全解

    golang的面向对象是不完备的面向对象,大部分人员都是从java转换而来,造成了很多的坑,这一篇文章记录我已知的golang面向对象性质和坑,希望后来者注意。
    

此文仅是个人在学习过程中摸索的自我总结,无法保证完全理解正确,如有错误,欢迎指正,共同学习!

基础

    go中没有class的概念,只有与之类似的struct结构体(很像c语言),还有接口interface

​ 接下来简单对比一下,他与普通面向对象语言的概念上的区别。

类/结构接口
gostruct
结构体
定义阶段只能描述成员变量
后面通过在函数前面增加标识符来完成方法的描述,可读性不如java
interface
接口
只能描述方法接口
javaclass

可以描述成员变量、方法
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中不能以之前的面向对象思维去看待它。