Go语言与面向对象编程

学习Go语言差不多快两个月了,感觉这个过程还是蛮快乐的,翻翻英文资料,写写小程序,总是觉得有好多东西都搞不明白,一步步走下来,却发现,这些迷惑好像也是不可或缺的。慢慢思考,最终也找到了解决方法,可能不是最好的,但是我会随着认识的不断深入,继续修改。

因为Java的东西在我的知识体系中是根深蒂固的,尽管我现在很讨厌java体系中的一些东西。对于曾经搞过java的人来说,去学习任何一门新的语言,首先会问,是否是面向对象的语言。刚接触GO的几天,曾经带我的师傅问我go是不是一门面向对象的语言,我闪烁其词,因为我还没有搞清楚到底是不是。面向对象思想是一种很好得方法(到目前来说我认为还是这样)。Go严格来说不是一门面向对象的语言,但是,其中的很多方式还是借鉴了面向对象的一些思想,尽管Go更接近C,但是从面向对象这块,从事java的开发人员还是不陌生的。

面向对象的三大特点:封装、继承、多态。

封装:就我自己的理解而言,封装就是将某些特征组装到一起,其他的对象或类可以使用这个整体,却不一定能够知道其实现细节。这又涉及到是否可见的问题。在java中,有privat,public,protected关键字来决定访问的权限。而在go中,采用了一种算是规定的方法:使用大小写来确定,大写是包外可见的,小写的struct或函数只能在包内使用。(go的关键字是及其少,简直可以说是吝啬,这也是其简洁的一个原因吧)。Java中,每个class都有其属性和方法,在go中,没有class的概念,与C语言一样,有struct的概念。与C语言不同的是,可以定义只用于这个struct的方法。看个例子会清晰很多。

// lxy project lxy.go 
package lxy  
import "strconv" 
type Student struct {  
    Name string  
    Age  int 
}  
func (s *Student) SetName(name string) {  
    s.Name = name  
}  
func (s *Student) GetName() string {  
 return s.Name  
}  
func (s *Student) SetAge(age int) {  
    s.Age = age  
}  
func (s *Student) GetAge() int {  
 return s.Age  
}  
func (s *Student) String() string {  
 return "name is " + s.Name + ",age is " + strconv.Itoa(s.Age)  
}  

先说一下在go中函数定义的格式吧。

func (ptype) funcName(a type)(btype){}

红色的func是定义函数的关键字,是不可变的,绿色部分是可选的,当然,如果有这部分,那就不是个函数了,而是一个方法。这种实现方式相当于是定义了,这个方法只用于这个type的实例。紫色部分代表的是函数的返回值。这个有点奇怪啊。向我们熟悉的语言,不管是java还是C,都会把返回值放在函数名前边,go却把它放到了最后。还有,go中的函数可以有0-n个返回值,并且可以指定返回值的名字。

结构体Student封装了两个方法,Name和Age,这两个熟悉包括结构体都是包外可以使用的。然后又给这个结构体定义了几个方法。看一下在主函数中的调用。

package main  
import (  
 "fmt" 
 "lxy" 
)  
func main() {  
    ss := new(lxy.Student)  
    ss.SetName("lxy")  
    ss.SetAge(20)  
    fmt.Println(ss.String())  
}  

在go中,要想调用另外一个package中的内容,需要首先import.在go中,所有的都会在一个特定的包中定义或者使用。使用方式为包名.(方法或者全局变量)

继承:在go中,没有明确说明继承的关键字,我使用的,我不确定是否是真正意义上的继承,姑且称之为模拟继承吧。还是看例子。

package main  
import (  
 "fmt" 
 "lxy" 
)  
type director struct {  
    lxy.Student  
    Name string  
}  
func (di *director) GetName() string {  
    fmt.Println("get director name")  
 return di.Name  
}  
func main() {  
    ss := new(lxy.Student)  
    ss.SetName("lxy")  
    ss.SetAge(20)  
    dd := new(director)  
    dd.Name = "director" 
    dd.Student = *ss  
    fmt.Println(dd.GetName())  
fmt.Println(dd.Student.GetName())  
fmt.Println(dd.GetAge())  
 
}  
还是刚才的代码,我只是在主函数所在的包中又增加了一个结构体,它包含了Student,而且是匿名包含的,这实际上就是一个继承。Director的实例可以直接调用Student中的所有方法,如最后一条语句。对于一些冲突的处理包括:像本例中,director中包含了GetName方法和Name属性,与Student中是完全一样的,因为这个没有出现在同一个level(何为同一个lever呢?本例中,director中的Name和lxy.Student就是同一个level),所以调用的是level最高的。若是处于同一个level的包含相同的变量名或者方法名,那就必须要明确指明了。比如
type A struct{
 a int
 b string
}
type B struct{
 a int
 c string
}
type C struct{
A
B
}

此时调用var cc C;cc.a 就错了,因为编译器不清楚到底是A还是B的a属性,这个需要指明是cc.A.a还是cc.B.a

多态:这个概念该怎么说呢,接口的多种不同实现方式。比如一个接口方法:叫。如果猫实现了这个接口,则结果会是“喵喵”;狗实现了这个方法,结果会是“汪汪”…..go中也有接口,只是其实现方式比较奇怪一些,闲话少说,还是拿例子说事:

// lxy project lxy.go 
package lxy  
import "strconv" 
type IPeople interface {  
    SetName(string)  
    GetName() string  
}  
type Student struct {  
    Name string  
    Age  int 
}  
type Teacher struct {  
    Name   string  
    Course string  
}  
func (s *Student) SetName(name string) {  
    s.Name = name  
}  
func (t *Teacher) SetName(name string) {  
    t.Name = name  
}  
func (s *Student) GetName() string {  
 return s.Name  
}  
func (t *Teacher) GetName() string {  
 return t.Name  
}  
func (s *Student) SetAge(age int) {  
    s.Age = age  
}  
func (s *Student) GetAge() int {  
 return s.Age  
}  
func (s *Student) String() string {  
 return "name is " + s.Name + ",age is " + strconv.Itoa(s.Age)  
}  
func (t *Teacher) SetCourse(course string) {  
    t.Course = course  
}  
func (t *Teacher) GetCourse() string {  
 return t.Course  
}  
代码中红色部分定义了一个接口IPeople,其包括两个方法。上述代码,我们可以得出一个结论,Teacher和Student都实现了接口IPeople.还没看出来是吧。因为Teacher和Student都实现了接口IPeople的两个方法。在go中,不需要显示声明某个struct是否声明了接口,只要其包括接口中的方法,go编译器就认为这个struct实现了这个接口了。即使接口与这个struct不在同一个包中,也会认为是实现了这个接口。看下主函数中:
[java] view plain copy print?
package main  
import (  
 "fmt" 
 "lxy" 
)  
type director struct {  
    lxy.Student  
    Name string  
}  
func (di *director) GetName() string {  
    fmt.Println("get director name")  
 return di.Name  
}  
func (di *director) SetName(name string) {  
    di.Name = name  
}  
func main() {  
    ss := new(lxy.Student)  
    ss.SetName("lxy")  
    ss.SetAge(20)  
    dd := new(director)  
    dd.Name = "director" 
    dd.Student = *ss  
    var ii lxy.IPeople  
    ii = dd  
    ii.SetName("test")  
    fmt.Println(ii.GetName())  
}  

一个struct可以实现多个接口,一个接口也可以被多个struct实现,这些与大家熟知的面向对象语言相同。接口在go语言中是很重要的一种结构,其意义远比在java中深远。具体的大家可以参考下go的学习资料。

差不多吧,我想到且知道的就是这些,虽然不是严格意义上的面向对象,但是最起码做到了神似。

原文发布于微信公众号 - Golang语言社区(Golangweb)

原文发表时间:2016-08-23

本文参与腾讯云自媒体分享计划,欢迎正在阅读的你也加入,一起分享。

发表于

我来说两句

0 条评论
登录 后参与评论

相关文章

来自专栏HTML5学堂

JS 计时器参数剖析与真题

HTML5学堂-码匠:计时器的第一个参数,包含几种不同的书写方法,可以是函数名,匿名函数,JS代码字符串,还有一些面试题当中会出现“函数调用”的书写方式。 那么...

35140
来自专栏机器之心

从Zero到Hero,一文掌握Python关键代码

选自free Code Camp 机器之心编译 本文整体梳理了 Python 的基本语法与使用方法,并重点介绍了对机器学习十分重要且常见的语法,如基本的条件、循...

34570
来自专栏菜鸟前端工程师

JavaScript学习笔记016-字符串方法0数组方法0值类型与引用型

8620
来自专栏数据结构与算法

P1865 A % B Problem

题目背景 题目名称是吸引你点进来的 实际上该题还是很水的 题目描述 区间质数个数 输入输出格式 输入格式: 一行两个整数 询问次数n,范围m 接下来n行...

32290
来自专栏程序员叨叨叨

8.1 函数第 8 章 函数与程序设计

通过第 5 章到第 7 章的阅读,我们已经知道了怎么声明变量(第 5 章),怎么写表达式和语句(第 6 章),怎么将输入 \ 输出参数绑定到语义词(第 7 章)...

10820
来自专栏工科狗和生物喵

【计算机本科补全计划】C++ Primer:指针和const限定符

正文之前 今天下午看了一下午的计算机组成与设计,结果好死不死的看到了设计部分--处理器的设计。天哪,我现在还只是一个准备给人装一台电脑做实验田的家伙,连用都不咋...

28940
来自专栏逸鹏说道

我为NET狂面试题-基础篇-答案

面向过程: 答案:图片只贴核心代码,完整代码请打开解决项目查看 (答案不唯一,官方答案只供参考,若有错误欢迎提出~) 99乘法表 https://githu...

377130
来自专栏Java帮帮-微信公众号-技术文章全总结

第九天 面向对象-类,封装,对象,private,this【悟空教程】

25560
来自专栏Golang语言社区

Go语言与面向对象编程

学习Go语言差不多快两个月了,感觉这个过程还是蛮快乐的,翻翻英文资料,写写小程序,总是觉得有好多东西都搞不明白,一步步走下来,却发现,这些迷惑好像也是不可或缺的...

46570
来自专栏诸葛青云的专栏

简述在C语言中, “字符”与“字符串”之间的区别

在C语言中,“字符”与“字符串”之间,是有区别的。这一篇文章中,我们将介绍一下,在C语言中的“字符”与“字符串”,它们之间的区别。

31130

扫码关注云+社区

领取腾讯云代金券