Go接口轻松入门:从基础到实用技巧

137 阅读4分钟

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嵌入 特性一同使用。
  • 同时使用组合和接口将构成非常强大的设计工具。

小测试

  1. 修改例子中laser类型的talk方法,阻止火星的激光枪发射,拯救人类免遭入侵。
  2. 扩展例子的代码,声明一个带有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(20128651700, 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(20128651700, 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.5859137.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编程之旅会越来越顺畅!