Go接口轻松入门:从基础到实用技巧
听说过Go语言的接口,却不知道从哪入手?别担心,Go的接口简单又强大,它能让你的代码更灵活、更优雅。不用繁琐声明,只需悄悄实现,就能解锁新玩法。本文将带你轻松入门,从基础用法到实用技巧,用有趣的例子和练习,帮你在Go世界迈出第一步!
本文是Go接口的入门宝典。从接口是什么、怎么用,到命名习惯和结构体的小妙招,我们用代码带你一步步上手。你还会发现它在标准库中的实用案例,比如如何让输出更漂亮。小测试和作业让你边学边练,快速掌握基础知识和实用技巧,写出更高效的Go代码!
接口
接口
- 接口关注于类型可以做什么,而不是存储了什么。
- 接口通过列举类型必须满足的一组方法来进行声明。
- 在Go语言中,不需要显式声明接口。
例子一
package main
var t interface {
talk() string
}
func main() {
}
例子二
package main
import (
"fmt"
"strings"
)
var t interface {
talk() string
}
type martian struct {}
func (m martian) talk() string {
return "nack nack"
}
type laser int
func (l laser) talk() string {
return strings.Repeat("pew ", int(l))
}
func main() {
t = martian{}
fmt.Println(t.talk())
t = laser(3)
fmt.Println(t.talk())
}
接口类型
- 为了复用,通常会把接口声明为类型。
- 按约定,接口名称通常以er结尾。
例子一
package main
import (
"fmt"
)
type talker interface {
talk() string
}
type martian struct {}
func (m martian) talk() string {
return "nack nack"
}
type laser int
func (l laser) talk() string {
return strings.Repeat("pew ", int(l))
}
func shout(t talker) {
louder := strings.ToUpper(t.talk())
fmt.Println(louder)
}
func main() {
shout(martian{})
shout(laser(2))
}
例子二
package main
import (
"fmt"
)
type talker interface {
talk() string
}
type martian struct {}
func (m martian) talk() string {
return "nack nack"
}
type laser int
func (l laser) talk() string {
return strings.Repeat("pew ", int(l))
}
func shout(t talker) {
louder := strings.ToUpper(t.talk())
fmt.Println(louder)
}
type starship struct {
laser
}
func main() {
s := starship{laser(3)}
fmt.Println(s.talk())
shout(s)
}
- 接口可以与struct嵌入 特性一同使用。
- 同时使用组合和接口将构成非常强大的设计工具。
小测试
- 修改例子中laser类型的talk方法,阻止火星的激光枪发射,拯救人类免遭入侵。
- 扩展例子的代码,声明一个带有talk方法的rover类型,并使方法返回“whir whir”,最后再使用shout函数处理这个新类型。
探索接口
- Go语言的接口都是隐式满足的。
例子一
package main
import (
"fmt"
"time"
)
// stardate returns a fictional measure of time for a given date.
func stardate(t time.Time) float64 {
doy := float64(t.YearDay())
h := float64(t.Hour()) / 24.0
return 1000 + doy + h
}
func main() {
day := time.Date(2012, 8, 6, 5, 17, 0, 0, time.UTC)
fmt.Printf("%.1f Curiosity has landed\n", stardate(day))
}
例子二
package main
import (
"fmt"
"time"
)
type stardater interface {
YearDay() int
Hour() int
}
type sol int
func (s sol) YearDay() int {
return int(s % 668)
}
func (s sol) Hour() int {
return 0
}
// stardate returns a fictional measure of time for a given date.
func stardate(t stardater) float64 {
doy := float64(t.YearDay())
h := float64(t.Hour()) / 24.0
return 1000 + doy + h
}
func main() {
day := time.Date(2012, 8, 6, 5, 17, 0, 0, time.UTC)
fmt.Printf("%.1f Curiosity has landed\n", stardate(day))
s := sol(1422)
fmt.Printf("%.1f Happy birthday\n", stardate(s))
}
小测试
- 隐式满足接口有什么好处?
满足接口
- Go标准库导出了很多只有单个方法的接口。
- Go通过简单的、通常只有单个方法的接口……来鼓励组合而不是继承,这些接口在各个组件之间形成了简明易懂的界限。---- Rob Pike
- 例如fmt包声明的Stringer接口:
type Stringer interface {String() string}
package main
import "fmt"
// location with a latitude, longitude in decimal degrees.
type location struct {
lat, long float64
}
// String formats a location with latitude, longitude.
func (l location) String() string {
return fmt.Sprintf("%v, %v", l.lat, l.long)
}
func main() {
curiosity := location{-4.5859, 137.4417}
fmt.Println(curiosity)
}
- 标准库中常用接口还包括:io.Reader,io.Writer,json.Marshaler...
小测试
- 请为coordinate类型编写String方法,并使用该方法以更为可读的方式打印出坐标数据。
type coordinate struct {
d, m, s float64
h rune
}
- 请使你的程序打印出:Elysium Planitia is at 4°30'0.0" N, 135°54'0.0" E
作业题
- 基于刚才小测试的答案,扩展并编写一个用JSON格式输出坐标的程序。这个程序的JSON输出应该分别用十进制(DD)和度/分/秒(DMS)两种格式提供坐标:
- 通过满足定制JSON数据的json.Marshaler接口,你应该无需修改坐标结构就能够达到上述目的。在编写MarshalJSON方法的时候可以考虑使用json.Marshal函数。
- 可以使用decimal方法
总结
Go接口虽小,却能大大提升代码质量。通过本文,你已经从零起步,学会了它的基础用法和一些实用技巧。隐式满足让设计更简单,嵌入和标准库应用让代码更灵活。现在,动手试试吧!把这些小技巧用起来,你的Go编程之旅会越来越顺畅!