Golang method详解

2019-03-02  本文已影响0人  北春南秋

  从严格意义上讲,Go语言不算一门面向对象的编程语言,至少没有提供关键字class,没有明确类的概念,更没有明确封装、继承、重载、多态等面向对象的概念。但这并不影响其面向对象的能力,取而代之的是其定义的struct概念,与C/C++中的struct不同,Go中的structtype特性承载了其所有面向对象的功能,本文中我们来看一下Go中method功能。

自定义类型

  在提及方法之前,先看一下Go中对自定义类型的支持,类似与C/C++中的typedef,Go中使用type关键字定义自定义类型。typeName可以是一个包或者函数内唯一的任何合法的Go标识符。typeSpecification可以是任何内置的类型(如string、int、切片、映射或者通道)、接口、结构体或者一个函数签名。

type typeName typeSpecification

  此处首先提出自定义类型,主要是为了下文铺垫,Go语言中规定,方法是作用在自定义类型的值上的一类特殊函数,通常自定义类型的值会被传递给该函数。该值可以以指针或者值的形式传递,这取决于方法如何定义。

方法

  如上文所述,方法只能作用在自定义类型的值,这其中有两层含义:

举例

package employee
  
import  "fmt"
  
type Employee struct {  
  
    FirstName   string
    LastName    string
    TotalLeaves int
    LeavesTaken int
}
  
func (e Employee) LeavesRemaining() {  
    fmt.Printf("%s %s has %d leaves remaining", e.FirstName, e.LastName, (e.TotalLeaves - e.LeavesTaken))
}

  上述代码中定义了一个结构体Employee,结构体中有一个方法LeaveRemaining这个方法计算并且显示剩余Employee的数目。然后可以调用上述方法。

package main
  
import "employee"
 
func main() {  
    e := employee.Employee {
        FirstName: "Sam",
        LastName: "Adolf",
        TotalLeaves: 30,
        LeavesTaken: 20,
    }
    e.LeavesRemaining()
}

运行结果如下

Sam Adolf has 10 leaves remaining  

模拟构造函数的例子

  Go不支持构造函数,编译器不接受结构体为空值的情况,此时可以通过一个支持的函数NewT(parameters) 来完成,这个实现类似C++中类的构造函数。如果一个包只定义了一种类型,那么可以通过New(parameters) 来代替NewT(parameters)。

package employee
 
 
import (  
    "fmt"
)
 
 
type employee struct {  
    firstName   string
    lastName    string
    totalLeaves int
    leavesTaken int
}
  
func New(firstName string, lastName string, totalLeave int, leavesTaken int) employee {  
    e := employee {firstName, lastName, totalLeave, leavesTaken}
    return e
}
  
func (e employee) LeavesRemaining() {  
    fmt.Printf("%s %s has %d leaves remaining", e.firstName, e.lastName, (e.totalLeaves - e.leavesTaken))
}

  在这里有一个重要的改变,通过结构体名首字母改为小写e,结构体名称从Employee变为employee,也就是将employee结构体限制为不可导出状态,从而阻止其他的包调用。通常设置一个不可导出的结构体所有成员都不能被导出是一个好的习惯(注意到上面的结构体各成员的首字母也变成小写了,如果一个结构体类型的名称以大写字母开头,则该结构体被导出,其他包可以访问它。同样地,如果结构体中的字段名以大写字母开头,则这些字段也可以被其他包访问)。其调用方式也需要同步修改。

package main  
 
import "oop/employee"
 
func main() {  
    e := employee.New("Sam", "Adolf", 30, 20)
    e.LeavesRemaining()
}

程序输入为

Sam Adolf has 10 leaves remaining 

method特性

封装特性

  如上文提到,Go区分公有属性和私有属性的机制就是方法或属性是否首字母大写,如果首字母大写的方法就是公有的,如果首字母小写的话就是私有的。此处不再举例说明。

继承特性

  Go中继承方式采用的是匿名组合的方式,如下述代码所示,Woman 结构体中包含匿名字段Person,那么Person中的属性也就属于Woman对象。

package main

import "fmt"

type Person struct {
    name string
}

type Woman struct {
    Person
    sex string
}

func main() {
    woman := Woman{Person{"wangwu"}, "女"}
    fmt.Println(woman.name)
    fmt.Println(woman.sex)
}

多态特性

package main

import "fmt"

type Eater interface {
    Eat()
}

type Man struct {
}

type Woman struct {
}

func (man *Man) Eat() {
    fmt.Println("Man Eat")
}

func (woman *Woman) Eat() {
    fmt.Println("Woman Eat")
}

func main() {
    var e Eater

    woman := Woman{}
    man := Man{}

    e = &woman
    e.Eat()

    e = &man
    e.Eat()
}

  Go中的多态功能其实是通过interface功能实现的,下一节再对其做详细介绍。

上一篇下一篇

猜你喜欢

热点阅读