Golang极简入门教程(二):方法和接口_Golang

方法

在 Golang 中没有类,不过我们可以为结构体定义方法。我们看一个例子:

复制代码 代码如下:

package main
 
import (
    "fmt"
    "math"
)
 
type Vertex struct {
    X, Y float64
}
 
// 结构体 Vertex 的方法
// 这里的方法接收者(method receiver)v 的类型为 *Vertex
func (v *Vertex) Abs() float64 {
    return math.Sqrt(v.X*v.X + v.Y*v.Y)
}
 
func main() {
    v := &Vertex{3, 4}
    fmt.Println(v.Abs())
}

在这里方法的接收者使用指针类型而非值类型主要出于以下几点考虑(类似 C/C++ 等语言):

1.避免方法每次调用时,对接收者的不必要的拷贝
2.在方法内可以修改接收者的值

我们可以为任意类型定义方法,但以下情况除外:

1.如果类型定义在其他包中,不能为其定义方法
2.如果类型是基础类型,不能为其定义方法

复制代码 代码如下:

package main
 
import (
    "fmt"
    "math"
)
 
// 定义一个类型 MyFloat
type MyFloat float64
 
// 注意此方法关联的类型是 MyFloat 而不是 *MyFloat
func (f MyFloat) Abs() float64 {
    if f < 0 {
        return float64(-f)
    }
    return float64(f)
}
 
func main() {
    f := MyFloat(-math.Sqrt2)
    fmt.Println(f.Abs())
}

接口(interface)

接口也是一种类型(就像结构体一样)。一个接口类型包含了一组方法,一个接口类型能够持有那些实现了这些方法的值。范例:

复制代码 代码如下:

// 定义接口 Abser
type Abser interface {
    Abs() float64
}
 
// 定义结构体 Vertex
type Vertex struct {
    X, Y float64
}
 
// 实现方法 Abs
func (v *Vertex) Abs() float64 {
    return math.Sqrt(v.X*v.X + v.Y*v.Y)
}
 
func main() {
    v := Vertex{3, 4}
    // 成功,能够持有 *Vertex 类型的值
    var a Abser = &v
    // 出错,不能持有 Vertex 类型的值
    // 因为在 *Vertex 上定义了方法 Abs,而未在 Vertex 上定义
    var b Abser = v
}

错误

Golang 提供了一个 error 接口:

复制代码 代码如下:

type error interface {
    Error() string
}

我们通过 os.Open 函数来了解一下 error 的用法:

复制代码 代码如下:

// 此函数用于打开一个文件
// 返回的第二个值为 error 类型
func Open(name string) (file *File, err error)

简单的例子:

复制代码 代码如下:

package main
 
import (
    "fmt"
    "os"
)
 
func main() {
    _, err := os.Open("test.txt")
    // 如果 err 不为 nil 表示存在错误
    if err != nil {
        fmt.Println(err)
    }
}

创建一个 error 值的最简单方式是使用 errors.New 函数:

复制代码 代码如下:

func Sqrt(f float64) (float64, error) {
    if f < 0 {
        // 出错时返回一个错误
        return 0, errors.New("math: square root of negative number")
    }
    // ...
}

我们也可以定义一个新的 error 的实现(也就是实现接口 error):

复制代码 代码如下:

type NegativeSqrtError float64
 
func (f NegativeSqrtError) Error() string {
    return fmt.Sprintf("math: square root of negative number %g", float64(f))
}

匿名域

结构体中可以存在只有类型而没有名字的域,它们被叫做匿名域。例如:

复制代码 代码如下:

struct {
    T1
    *T2
}

一个结构体的匿名域中的域或者方法可以被此结构体实例直接访问:

复制代码 代码如下:

package main
 
import "fmt"
 
type Car struct {
    wheelCount int
}
 
func (car *Car) numberOfWheels() int {
    return car.wheelCount
}
 
type Ferrari struct {
    Car
}
 
func main() {
    f := Ferrari{Car{4}}
    fmt.Println("A Ferrari has this many wheels: ", f.numberOfWheels())
}

时间: 2024-12-24 21:54:31

Golang极简入门教程(二):方法和接口_Golang的相关文章

Golang极简入门教程(四):编写第一个项目_Golang

workspace Golang 的代码必须放置在一个 workspace 中.一个 workspace 是一个目录,此目录中包含几个子目录: 1.src 目录.包含源文件,源文件被组织为包(一个目录一个包) 2.pkg 目录.包含包对象(package objects) 3.bin 目录.包含可执行的命令 包源文件(package source)被编译为包对象(package object),命令源文件(command source)被编译为可执行命令(command executable).

Golang极简入门教程(三):并发支持_Golang

Golang 运行时(runtime)管理了一种轻量级线程,被叫做 goroutine.创建数十万级的 goroutine 是没有问题的.范例: 复制代码 代码如下: package main   import (     "fmt"     "time" )   func say(s string) {     for i := 0; i < 5; i++ {         time.Sleep(100 * time.Millisecond)       

Golang极简入门教程(一):基本概念_Golang

安装 Golang 在 http://golang.org/dl/ 可以下载到 Golang.安装文档:http://golang.org/doc/install. Hello Go 我们先创建一个文件 hello.go: 复制代码 代码如下: package main   import "fmt"   func main() {     fmt.Printf("hello Golang\n"); } 执行此程序: 复制代码 代码如下: go run hello.g

JavaScript极简入门教程(二):对象和函数_javascript技巧

阅读本文需要有其他语言的编程经验. JavaScript 中的简单类型包括: 1.数字 2.字符串 3.布尔(true 和 false) 4.null 5.undefined 此外的其他类型均是对象(我们不要被 typeof 操作符的返回值所迷惑),例如: 1.函数 2.数组 3.正则表达式 4.对象(对象自然也是对象) 对象基础 在 JavaScript 中,对象是属性的集合(对象为关联数组),每个属性包括: 1.属性名,必须为字符串 2.属性值,可以为除了 undefined 之外的任何值

Nodejs极简入门教程(二):定时器_node.js

setTimeout 和 clearTimeout 复制代码 代码如下: var obj = setTimeout(cb, ms); setTimeout 用于设置一个回调函数 cb,其在最少 ms 毫秒后被执行(并非在 ms 毫秒后马上执行).setTimeout 返回值可以作为 clearTimeout 的参数,clearTimeout 用于停止定时器,这样回调函数就不会被执行了. setInterval 和 clearInterval 复制代码 代码如下: var obj = setInt

Nodejs极简入门教程(三):进程_node.js

Node 虽然自身存在多个线程,但是运行在 v8 上的 JavaScript 是单线程的.Node 的 child_process 模块用于创建子进程,我们可以通过子进程充分利用 CPU.范例: 复制代码 代码如下: var fork = require('child_process').fork; // 获取当前机器的 CPU 数量 var cpus = require('os').cpus(); for (var i = 0; i < cpus.length; i++) {     // 生

Nodejs极简入门教程(一):模块机制_node.js

JavaScript 规范(ECMAScript)没有定义一套完善的能适用于大多数程序的标准库.CommonJS 提供了一套 JavaScript 标准库规范.Node 实现了 CommonJS 规范. 模块基础 在 Node 中,模块和文件是一一对应的.我们定义一个模块: 复制代码 代码如下: // circle.js var PI = Math.PI;   // 导出函数 area exports.area = function(r) {     return PI * r * r; }  

JavaScript极简入门教程(一):基础篇_javascript技巧

阅读本文需要有其他语言的编程经验. 开始学习之前 大多数的编程语言都存在好的部分和差的部分.本文只讲述 JavaScript 中好的部分,这是因为: 1.仅仅学习好的部分能够缩短学习时间 2.编写的代码更加健壮 3.编写的代码更加易读 4.编写的代码更加易于维护 弱类型和强类型 通常来说,越早的修复错误,为之付出的代价就越小.强类型语言的编译器可以在编译时检查某些错误.而 JavaScript 是一门弱类型语言,其解释器无法检查类型错误,但实践表明: 1.强类型能够避免的错误并不是那些关键性错误

JavaScript极简入门教程(三):数组_javascript技巧

阅读本文需要有其他语言的编程经验. 在 JavaScript 中数组是对象(而非线性分配的内存). 通过数组 literal 来创建数组: 复制代码 代码如下: var empty = []; var numbers = [     'zero', 'one', 'two', 'three', 'four',     'five', 'six', 'seven', 'eight', 'nine' ]; empty[1] // undefined numbers[1] // 'one' empty