第五章:接口与多态
5.1 接口基础
什么是接口
接口是一组方法签名的集合,定义了对象的行为。
package main
import "fmt"
// 定义接口
type Speaker interface {
Speak() string
}
type Mover interface {
Move() string
}
// 实现接口的类型
type Dog struct {
Name string
}
func (d Dog) Speak() string {
return "汪汪!"
}
func (d Dog) Move() string {
return "四条腿跑"
}
type Cat struct {
Name string
}
func (c Cat) Speak() string {
return "喵喵~"
}
func (c Cat) Move() string {
return "悄无声息地走"
}
// 多态函数
func MakeSpeak(s Speaker) {
fmt.Println(s.Speak())
}
func main() {
dog := Dog{Name: "旺财"}
cat := Cat{Name: "咪咪"}
// 多态调用
MakeSpeak(dog) // 汪汪!
MakeSpeak(cat) // 喵喵~
// 接口变量
var s Speaker
s = dog
fmt.Println(s.Speak())
s = cat
fmt.Println(s.Speak())
}5.2 空接口
package main
import "fmt"
// 空接口可以接收任何类型
func printAny(v interface{}) {
fmt.Printf("值: %v, 类型: %T\n", v, v)
}
// 类型断言
func process(v interface{}) {
// 方式1:直接断言
if str, ok := v.(string); ok {
fmt.Println("是字符串:", str)
return
}
// 方式2:switch类型判断
switch val := v.(type) {
case int:
fmt.Println("是整数:", val)
case float64:
fmt.Println("是浮点数:", val)
case bool:
fmt.Println("是布尔值:", val)
case []int:
fmt.Println("是整数切片:", val)
default:
fmt.Println("未知类型:", val)
}
}
func main() {
printAny("hello")
printAny(123)
printAny(3.14)
printAny(true)
printAny([]int{1, 2, 3})
fmt.Println("---")
process("test")
process(100)
process(3.14)
process([]int{1, 2, 3})
}5.3 接口组合
package main
import "fmt"
// 基础接口
type Reader interface {
Read() string
}
type Writer interface {
Write(string)
}
// 接口组合
type ReadWriter interface {
Reader
Writer
}
// 实现
type File struct {
Content string
}
func (f *File) Read() string {
return f.Content
}
func (f *File) Write(data string) {
f.Content = data
}
func main() {
file := &File{}
// 可以作为ReadWriter使用
var rw ReadWriter = file
rw.Write("Hello, World!")
fmt.Println(rw.Read())
// 也可以单独作为Reader或Writer
var r Reader = file
var w Writer = file
fmt.Println(r.Read())
w.Write("New content")
}5.4 接口值与nil
package main
import "fmt"
type Person struct {
Name string
}
func (p *Person) Speak() string {
if p == nil {
return "我是nil"
}
return "我叫" + p.Name
}
type Speaker interface {
Speak() string
}
func main() {
var p *Person // nil指针
// 接口值不是nil,但内部指针是nil
var s Speaker = p
fmt.Println(s != nil) // true
fmt.Println(s.Speak()) // 我是nil
// 真正的nil接口
var s2 Speaker
fmt.Println(s2 == nil) // true
// s2.Speak() // panic!
}5.5 常用标准库接口
package main
import (
"bytes"
"fmt"
"io"
"os"
"strings"
)
func main() {
// fmt.Stringer接口
type Person struct {
Name string
Age int
}
// io.Reader/io.Writer
var buf bytes.Buffer
buf.WriteString("Hello, ")
buf.WriteString("World!")
fmt.Println(buf.String())
// 标准输入输出
// io.Copy(os.Stdout, os.Stdin)
// strings.Reader
r := strings.NewReader("Hello")
data := make([]byte, 5)
r.Read(data)
fmt.Println(string(data))
// error接口
err := fmt.Errorf("错误信息: %s", "出错了")
fmt.Println(err)
}5.6 项目实战:形状计算
package main
import (
"fmt"
"math"
)
// 形状接口
type Shape interface {
Area() float64
Perimeter() float64
}
// 圆形
type Circle struct {
Radius float64
}
func (c Circle) Area() float64 {
return math.Pi * c.Radius * c.Radius
}
func (c Circle) Perimeter() float64 {
return 2 * math.Pi * c.Radius
}
// 矩形
type Rectangle struct {
Width, Height float64
}
func (r Rectangle) Area() float64 {
return r.Width * r.Height
}
func (r Rectangle) Perimeter() float64 {
return 2 * (r.Width + r.Height)
}
// 三角形
type Triangle struct {
A, B, C float64 // 三边
}
func (t Triangle) Area() float64 {
// 海伦公式
s := t.Perimeter() / 2
return math.Sqrt(s * (s - t.A) * (s - t.B) * (s - t.C))
}
func (t Triangle) Perimeter() float64 {
return t.A + t.B + t.C
}
// 多态函数
func PrintShapeInfo(s Shape) {
fmt.Printf("面积: %.2f, 周长: %.2f\n", s.Area(), s.Perimeter())
}
// 计算总面积
func TotalArea(shapes []Shape) float64 {
total := 0.0
for _, s := range shapes {
total += s.Area()
}
return total
}
func main() {
shapes := []Shape{
Circle{Radius: 5},
Rectangle{Width: 10, Height: 5},
Triangle{A: 3, B: 4, C: 5},
}
for _, s := range shapes {
PrintShapeInfo(s)
}
fmt.Printf("总面积: %.2f\n", TotalArea(shapes))
}