文档章节

Go方法——方法声明

秋风醉了
 秋风醉了
发布于 2016/07/09 17:28
字数 1215
阅读 41
收藏 1

Go方法——方法声明

在函数声明时,在其名字之前放上一个变量,即是一个方法。这个附加的参数会将该函数附加到这种类型上,即相当于为这种类型定义了一个独占的方法。

下面来写我们第一个方法的例子,

package main

import (
	"fmt"
	"math"
)

func main() {

	p := Point{1.2, 2.0}

	q := Point{2.0, 4.0}

	res := Distance(p, q) //包级别的 Distance 函数

	fmt.Println(res)

	res2 := p.Distance(q) //类型级别的 Distance 方法

	fmt.Println(res2)
}

type Point struct{ X, Y float64 }

// traditional function
func Distance(p, q Point) float64 {
	return math.Hypot(q.X-p.X, q.Y-p.Y)
}

// same thing, but as a method of the Point type
func (p Point) Distance(q Point) float64 {
	return math.Hypot(q.X-p.X, q.Y-p.Y)
}


上面的代码里那个附加的参数p,叫做方法的接收器(receiver),早期的面向对象语言留下的遗产将调用一个方法称为“向一个对象发送消息”。

在Go语言中,我们并不会像其它语言那样用this或者self作为接收器;我们可以任意的选择接收器的名字。由于接收器的名字经常会被使用到,所以保持其在方法间传递时的一致性和简短性是不错的主意。这里的建议是可以使用其类型的第一个字母,比如这里使用了Point的首字母p。

在方法调用过程中,接收器参数一般会在方法名之前出现。这和方法声明是一样的,都是接收器参数在方法名字之前。下面是例子:

p := Point{1, 2}
q := Point{4, 6}
fmt.Println(Distance(p, q)) // "5", function call
fmt.Println(p.Distance(q))  // "5", method call

可以看到,上面的两个函数调用都是Distance,但是却没有发生冲突。第一个Distance的调用实际上用的是包级别的函数geometry.Distance,而第二个则是使用刚刚声明的Point,调用的是Point类下声明的Point.Distance方法。

这种p.Distance的表达式叫做选择器,因为他会选择合适的对应p这个对象的Distance方法来执行。选择器也会被用来选择一个struct类型的字段,比如p.X。由于方法和字段都是在同一命名空间,所以如果我们在这里声明一个X方法的话,编译器会报错,因为在调用p.X时会有歧义(译注:这里确实挺奇怪的)。

因为每种类型都有其方法的命名空间,我们在用Distance这个名字的时候,不同的Distance调用指向了不同类型里的Distance方法。让我们来定义一个Path类型,这个Path代表一个线段的集合,并且也给这个Path定义一个叫Distance的方法。

package main

import (
	"fmt"
	"math"
)

func main() {

	p := Point{1.2, 2.0}

	q := Point{2.0, 4.0}

	res := Distance(p, q) //包级别的 Distance 函数

	fmt.Println(res)

	res2 := p.Distance(q) //类型级别的 Distance 方法

	fmt.Println(res2)

	perim := Path{
		{1, 1},
		{5, 1},
		{5, 4},
		{1, 1},
	}

	fmt.Println(perim.Distance()) // "12"

}

type Point struct{ X, Y float64 }

// traditional function
func Distance(p, q Point) float64 {
	return math.Hypot(q.X-p.X, q.Y-p.Y)
}

// same thing, but as a method of the Point type
func (p Point) Distance(q Point) float64 {
	return math.Hypot(q.X-p.X, q.Y-p.Y)
}

type Path []Point

func (path Path) Distance() float64 {
	sum := 0.0
	for i := range path {
		if i > 0 {
			sum += path[i-1].Distance(path[i])
		}
	}
	return sum
}

Path是一个命名的slice类型,而不是Point那样的struct类型,然而我们依然可以为它定义方法。在能够给任意类型定义方法这一点上,Go和很多其它的面向对象的语言不太一样。因此在Go语言里,我们为一些简单的数值、字符串、slice、map来定义一些附加行为很方便。方法可以被声明到任意类型,只要不是一个指针或者一个interface。

两个Distance方法有不同的类型。他们两个方法之间没有任何关系,尽管Path的Distance方法会在内部调用Point.Distance方法来计算每个连接邻接点的线段的长度。

让我们来调用一个新方法,计算三角形的周长:

perim := Path{
    {1, 1},
    {5, 1},
    {5, 4},
    {1, 1},
}
fmt.Println(perim.Distance()) // "12"

在上面两个对Distance名字的方法的调用中,编译器会根据方法的名字以及接收器来决定具体调用的是哪一个函数。第一个例子中path[i-1]数组中的类型是Point,因此Point.Distance这个方法被调用;在第二个例子中perim的类型是Path,因此Distance调用的是Path.Distance。

对于一个给定的类型,其内部的方法都必须有唯一的方法名,**但是不同的类型却可以有同样的方法名,比如我们这里Point和Path就都有Distance这个名字的方法;**所以我们没有必要非在方法名之前加类型名来消除歧义,比如PathDistance。

==========END==========

© 著作权归作者所有

共有 人打赏支持
秋风醉了
粉丝 241
博文 566
码字总数 417296
作品 0
朝阳
程序员
私信 提问
编写高质量代码改善C#程序的157个建议[为泛型指定初始值、使用委托声明、使用Lambda替代方法和匿名方法]

前言   泛型并不是C#语言一开始就带有的特性,而是在FCL2.0之后实现的新功能。基于泛型,我们得以将类型参数化,以便更大范围地进行代码复用。同时,它减少了泛型类及泛型方法中的转型,确...

aehyok
2014/05/15
0
0
轻松学习 JavaScript——第 8 部分:JavaScript 中的类

1. 程序员必读24本经典,100%免费领取 2. 最值得学习的24本编程书,免费领取 3. 24本编程纸质书籍,云沃客限时免费送! 4. 助力程序员快速涨薪的24本精选纸质书 本文由码农网 – 小峰原创翻译...

3. 24本编程纸质书籍,云沃客限时免费送!
2018/01/06
0
0
golang函数——可以为类型(包括内置数据类型)定义函数,类似类方法,同时支持多返回值

不可或缺的函数,在Go中定义函数的方式如下: 通过函数定义,我们可以看到Go中函数和其他语言中的共性和特性 共性 关键字——func 方法名——funcName 入参——— a,b int,b string 返回值—...

桃子红了呐
2017/11/16
0
0
Java中类与方法的学习笔记(一):

关于JAVA中类与方法的学习笔记(一): (笔者是一个计算机的学生,四年没怎么用功,所以在JAVA语言的认知上很模糊,接近小白但又不是小白。下面的总结记录完全是按照笔者个人的认知范围和重...

萧沐垚
2016/12/16
5
0
成员变量和局部变量有什么区别?

FAQ2.13 成员变量和局部变量有什么区别? class A { int a;//成员变量 public static void main(String[] args) { int b;//局部变量 } } —————————————————————————...

郭二翔
2011/12/17
0
0

没有更多内容

加载失败,请刷新页面

加载更多

2019-1-16

2019-1-16 星期三 晴转霾 早饭:小面包+鸡蛋糕;午饭:馍+地三鲜;晚饭:; 6:50起床,因为媳妇说可能今天晚上去大雁塔那边吃饭,早上起来后洗了个澡(主要因为头发很油了)。 今天早上天气...

莱菔籽
8分钟前
0
0
localDate、localDateTime、localTime的使用

从前端接受的时候,localDate类型的数据要转换,加 @DateTimeFormat(pattern = "yyyy-MM-dd")

shimmerkaiye
15分钟前
1
0
1.二叉树

概念 二叉树(binary tree)是每个节点最多只有两个分支(即不存在分支度大于2的节点)的结构树。通常分支被称为“左子树”和“右子树”,左子树和右子树的位置不能随意颠倒。二叉树的第i层 ...

火拳-艾斯
18分钟前
2
0
java 线程

一、通过实现Runnable接口来创建线程 public class TestThread implements Runnable { public void run() { try { for (int i = 0; i < 10; i++) { ......

朝如青丝暮成雪
23分钟前
1
0
关于eclipse2017 import javax.servlet.jsp.tagext引入错误得问题

在eclipse中: 这个javax.servlet.jsp.tagext属于是tomcat相关jar包找到jsp-api.jar 在tomcat文件夹下边的lib文件夹中就有 如果项目中报错的话 把这个加入到项目中 在myeclipse中: 如下图,...

ZhangLG
37分钟前
4
0

没有更多内容

加载失败,请刷新页面

加载更多

返回顶部
顶部