设为首页 收藏本站
查看: 975|回复: 0

[经验分享] GoLang之方法与接口

[复制链接]

尚未签到

发表于 2018-9-19 13:43:29 | 显示全部楼层 |阅读模式
GoLang之方法与接口
  Go语言没有沿袭传统面向对象编程中的诸多概念,比如继承、虚函数、构造函数和析构函数、隐藏的this指针等。

方法
  Go 语言中同时有函数和方法。方法就是一个包含了接受者(receiver)的函数,receiver可以是内置类型或者结构体类型的一个值或者是一个指针。所有给定类型的方法属于该类型的方法集。
  如下面的这个例子,定义了一个新类型Integer,它和int一样,只是为它内置的int类型增加了个新方法Less()
  

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指针,而是在定义成员方法时显式声明了其所属的对象。
  method的语法如下:
  

func (r ReceiverType) funcName(parameters) (results)  

  当调用method时,会将receiver作为函数的第一个参数:
  

funcName(r, parameters);  

  所以,receiver是值类型还是指针类型要看method的作用。如果要修改对象的值,就需要传递对象的指针。
  指针作为Receiver会对实例对象的内容发生操作,而普通类型作为Receiver仅仅是以副本作为操作对象,并不对原实例对象发生操作。
  

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语言函数的参数也是基于值传递。
  注意:当方法的接受者是指针时,即使用值类型调用那么方法内部也是对指针的操作。
  之前说过,Go语言没有构造函数的概念,通常使用一个全局函数来完成。例如:
  

func NewRect(x, y,>
return&Rect{x, y,>
}  

  
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语言中的大多数类型都基于值语义,包括:


  • 基本类型,如byte、int、bool、float32、string等;
  • 复合类型,如arry、struct、pointer等;
  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。
  参考文档:
  http://se77en.cc/2014/05/05/methods-interfaces-and-embedded-types-in-golang/
  http://se77en.cc/2014/05/04/choose-whether-to-use-a-value-or-pointer-receiver-on-methods/



运维网声明 1、欢迎大家加入本站运维交流群:群②:261659950 群⑤:202807635 群⑦870801961 群⑧679858003
2、本站所有主题由该帖子作者发表,该帖子作者与运维网享有帖子相关版权
3、所有作品的著作权均归原作者享有,请您和我们一样尊重他人的著作权等合法权益。如果您对作品感到满意,请购买正版
4、禁止制作、复制、发布和传播具有反动、淫秽、色情、暴力、凶杀等内容的信息,一经发现立即删除。若您因此触犯法律,一切后果自负,我们对此不承担任何责任
5、所有资源均系网友上传或者通过网络收集,我们仅提供一个展示、介绍、观摩学习的平台,我们不对其内容的准确性、可靠性、正当性、安全性、合法性等负责,亦不承担任何法律责任
6、所有作品仅供您个人学习、研究或欣赏,不得用于商业或者其他用途,否则,一切后果均由您自己承担,我们对此不承担任何法律责任
7、如涉及侵犯版权等问题,请您及时通知我们,我们将立即采取措施予以解决
8、联系人Email:admin@iyunv.com 网址:www.yunweiku.com

所有资源均系网友上传或者通过网络收集,我们仅提供一个展示、介绍、观摩学习的平台,我们不对其承担任何法律责任,如涉及侵犯版权等问题,请您及时通知我们,我们将立即处理,联系人Email:kefu@iyunv.com,QQ:1061981298 本贴地址:https://www.yunweiku.com/thread-594326-1-1.html 上篇帖子: go get 获得 golang.org 的项目 下篇帖子: 『Golang』在Golang中使用json
您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

扫码加入运维网微信交流群X

扫码加入运维网微信交流群

扫描二维码加入运维网微信交流群,最新一手资源尽在官方微信交流群!快快加入我们吧...

扫描微信二维码查看详情

客服E-mail:kefu@iyunv.com 客服QQ:1061981298


QQ群⑦:运维网交流群⑦ QQ群⑧:运维网交流群⑧ k8s群:运维网kubernetes交流群


提醒:禁止发布任何违反国家法律、法规的言论与图片等内容;本站内容均来自个人观点与网络等信息,非本站认同之观点.


本站大部分资源是网友从网上搜集分享而来,其版权均归原作者及其网站所有,我们尊重他人的合法权益,如有内容侵犯您的合法权益,请及时与我们联系进行核实删除!



合作伙伴: 青云cloud

快速回复 返回顶部 返回列表