1. 程式人生 > >Go學習(10):方法

Go學習(10):方法

一.方法

1.1 什麼是方法

Go 語言中同時有函式和方法。一個方法就是一個包含了接受者的函式,接受者可以是命名型別或者結構體型別的一個值或者是一個指標。所有給定型別的方法屬於該型別的方法集

方法只是一個函式,它帶有一個特殊的接收器型別,它是在func關鍵字和方法名之間編寫的。接收器可以是struct型別或非struct型別。接收方可以在方法內部訪問。

1.2 方法的語法

定義方法的語法

func (t Type) methodName(parameter list) {  
}

例項程式碼:

package main

import (  
    "fmt"
) type Employee struct { name string salary int currency string } /* displaySalary() method has Employee as the receiver type */ func (e Employee) displaySalary() { fmt.Printf("Salary of %s is %s%d", e.name, e.currency, e.salary) } func main() { emp1 := Employee { name:
"Sam Adolf", salary: 5000, currency: "$", } emp1.displaySalary() //Calling displaySalary() method of Employee type }

可以定義相同的方法名

示例程式碼:

package main

import (
	"fmt"
	"math"
)

type Rectangle struct {
	width, height float64
}
type Circle struct {
	radius float64
}


func
(r Rectangle) area() float64 { return r.width * r.height } //該 method 屬於 Circle 型別物件中的方法 func (c Circle) area() float64 { return c.radius * c.radius * math.Pi } func main() { r1 := Rectangle{12, 2} r2 := Rectangle{9, 4} c1 := Circle{10} c2 := Circle{25} fmt.Println("Area of r1 is: ", r1.area()) fmt.Println("Area of r2 is: ", r2.area()) fmt.Println("Area of c1 is: ", c1.area()) fmt.Println("Area of c2 is: ", c2.area()) }

執行結果

Area of r1 is:  24
Area of r2 is:  36
Area of c1 is:  314.1592653589793
Area of c2 is:  1963.4954084936207
  • 雖然method的名字一模一樣,但是如果接收者不一樣,那麼method就不一樣
  • method裡面可以訪問接收者的欄位
  • 呼叫method通過.訪問,就像struct裡面訪問欄位一樣

1.3 方法和函式

既然我們已經有了函式,為什麼還要使用方法?

示例程式碼:

package main

import (  
    "fmt"
)

type Employee struct {  
    name     string
    salary   int
    currency string
}

/*
 displaySalary() method converted to function with Employee as parameter
*/
func displaySalary(e Employee) {  
    fmt.Printf("Salary of %s is %s%d", e.name, e.currency, e.salary)
}

func main() {  
    emp1 := Employee{
        name:     "Sam Adolf",
        salary:   5000,
        currency: "$",
    }
    displaySalary(emp1)
}

在上面的程式中,displaySalary方法被轉換為一個函式,而Employee struct作為引數傳遞給它。這個程式也產生了相同的輸出:Salary of Sam Adolf is $5000.。

為什麼我們可以用函式來寫相同的程式呢?有以下幾個原因

  1. Go不是一種純粹面向物件的程式語言,它不支援類。因此,型別的方法是一種實現類似於類的行為的方法。
  2. 相同名稱的方法可以在不同的型別上定義,而具有相同名稱的函式是不允許的。假設我們有一個正方形和圓形的結構。可以在正方形和圓形上定義一個名為Area的方法。這是在下面的程式中完成的。

1.4 變數作用域

作用域為已宣告識別符號所表示的常量、型別、變數、函式或包在原始碼中的作用範圍。

Go 語言中變數可以在三個地方宣告:

  • 函式內定義的變數稱為區域性變數
  • 函式外定義的變數稱為全域性變數
  • 函式定義中的變數稱為形式引數

區域性變數

在函式體內宣告的變數稱之為區域性變數,它們的作用域只在函式體內,引數和返回值變數也是區域性變數。

全域性變數

在函式體外宣告的變數稱之為全域性變數,首字母大寫全域性變數可以在整個包甚至外部包(被匯出後)使用。

package main

import "fmt"

/* 宣告全域性變數 */
var g int

func main() {

   /* 宣告區域性變數 */
   var a, b int

   /* 初始化引數 */
   a = 10
   b = 20
   g = a + b

   fmt.Printf("結果: a = %d, b = %d and g = %d\n", a, b, g)
}

結果

結果: a = 10, b = 20 and g = 30

形式引數

形式引數會作為函式的區域性變數來使用

指標作為接收者

若不是以指標作為接收者,實際只是獲取了一個copy,而不能真正改變接收者的中的資料

func (b *Box) SetColor(c Color) {
	b.color = c
}

示例程式碼

package main

import (
	"fmt"
)

type Rectangle struct {
	width, height int
}

func (r *Rectangle) setVal() {
	r.height = 20
}

func main() {
	p := Rectangle{1, 2}
	s := p
	p.setVal()
	fmt.Println(p.height, s.height)
}

結果

20 2

如果沒有那個*,則值就是2 2

1.5 method繼承

method是可以繼承的,如果匿名欄位實現了一個method,那麼包含這個匿名欄位的struct也能呼叫該method

package main

import "fmt"

type Human struct {
	name  string
	age   int
	phone string
}
type Student struct {
	Human  //匿名欄位
	school string
}
type Employee struct {
	Human   //匿名欄位
	company string
}

func (h *Human) SayHi() {
	fmt.Printf("Hi, I am %s you can call me on %s\n", h.name, h.phone)
}
func main() {
	mark := Student{Human{"Mark", 25, "222-222-YYYY"}, "MIT"}
	sam := Employee{Human{"Sam", 45, "111-888-XXXX"}, "Golang Inc"}
	mark.SayHi()
	sam.SayHi()
}

執行結果:

Hi, I am Mark you can call me on 222-222-YYYY
Hi, I am Sam you can call me on 111-888-XXXX

1.6 method重寫

package main

import "fmt"

type Human struct {
	name  string
	age   int
	phone string
}
type Student struct {
	Human  //匿名欄位
	school string
}
type Employee struct {
	Human   //匿名欄位
	company string
}

//Human定義method
func (h *Human) SayHi() {
	fmt.Printf("Hi, I am %s you can call me on %s\n", h.name, h.phone)
}

//Employee的method重寫Human的method
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) //Yes you can split into 2 lines here.
}
func main() {
	mark := Student{Human{"Mark", 25, "222-222-YYYY"}, "MIT"}
	sam := Employee{Human{"Sam", 45, "111-888-XXXX"}, "Golang Inc"}
	mark.SayHi()
	sam.SayHi()
}

執行結果:

Hi, I am Mark you can call me on 222-222-YYYY
Hi, I am Sam, I work at Golang Inc. Call me on 111-888-XXXX
  • 方法是可以繼承和重寫的
  • 存在繼承關係時,按照就近原則,進行呼叫