go语言接口学习

09 Apr 2016 go语言接口学习

刚开始学习go语言的时候,看代码中某个类型实现了Error()方法,然后就可以将该类型赋值给error类型。当时还没学习接口,不懂为什么可以这样赋值。学习接口以后,才明白怎么回事。查看go源码,发现内置类型error其实是一个接口类型,并实现了Error()方法,如下:

type error interface {
    Error() string
}

所以任何类型,只要实现了Error()方法,就可以将该类型的值赋值给error类型。

go语言提供了一种接口类型interface,通过接口可以实现面向对象中一些特性,例如多态。go的接口只是一组方法的声明,抽象的定了对象的行为,并不具体实现。例如:

type Shaper interface {
    Area() int
    Perimeter() int
}

go语言中的接口都很简短,通常它们会包含0个、最多3个方法。这里定义了一个接口类型Shaper,并声明了2个方法Area()和Perimeter(),但是未给出Area()和Perimeter()方法的具体实现。如果某个类型实现了Area()和Perimeter()方法,就可以说该类型实现了Shaper接口,于是可以将该类型的实例赋值给Shaper类型变量。例如:

type Square struct {
    a int
}

func (s *Square) Area() int {
    return s.a * s.a
}

func (s *Square) Perimeter() int {
    return s.a * 4
}

square := new(Square)
square.a = 3

var shape Shaper
shape = square

这里定义了一个结构体Square,并实现了Area()和Perimeter()方法。然后就可以将Shaper类型的值赋给接口shape。即任何类型,只要实现了Area()和Perimeter()方法,都可以将值赋值给Shaper类型变量。然后通过该接口类型,就可以调用相应类型的Area()和Perimeter()方法。即实现了同一种类型在不同的实例上表现不同的行为。go通过接口实现了duck-typing。如果一个对象走路像鸭子,游泳也像鸭子,叫声也像鸭子,那么该对象就可以被称作为鸭子。这里的Square类型实现了Area()和Perimeter()方法,所以它也可以称作是Shaper类型。

特别需要指出的是,go提供了一种类似c语言中的void*类型,即空接口。空接口不包含任何方法。可以存储任意类型的数值,当我们需要存储任意类型的值时很有用。例如:

type AnyShape interface{}
var anyShape AnyShape
anyShape = square

声明了一个空接口类型anyShape,可以将Square类型的square赋值给它。但是在使用过程中需要进行类型断言,主要有2中方式,一个是使用switch,另外一种方式是使用if语句。

下面给出一个完整的示例,该示例定义了三个类型Square、Rectangle和RightTriangle。分别对应正方形、矩形和直角三角形。并在相应的类型上实现了求面积和求周长的方法:Area()和Perimeter()。然后定义了一个接口类型Shaper,并声明了方法Area()和Perimeter()。即三个类型Square、Rectangle和RightTriangle都实现了接口Shaper。然后定义了一个空接口AnyShape类型。通过switch和if简单实现了接口的类型断言。最后使用结构体实例、接口Shaper和空接口AnyShape调用Area()方法实现了计算不同图形的面积。

package main

import (
    "fmt"
)

type Square struct {
    a int
}

func (s *Square) Area() int {
    return s.a * s.a
}

func (s *Square) Perimeter() int {
    return s.a * 4
}

type Rectangle struct {
    a int
    b int
}

func (r *Rectangle) Area() int {
    return r.a * r.b
}

func (r *Rectangle) Perimeter() int {
    return (r.a + r.b) * 2
}

type RightTriangle struct {
    a int
    b int
    // c is hypotenuse
    c int
}

func (r *RightTriangle) Area() int {
    return r.a * r.b / 2
}

func (r *RightTriangle) Perimeter() int {
    return r.a + r.b + r.c
}

type Shaper interface {
    Area() int
    Perimeter() int
}

type AnyShape interface{}

func main() {

    square := new(Square)
    square.a = 12

    rectangle := new(Rectangle)
    rectangle.a = 12
    rectangle.b = 5

    rightTriangle := new(RightTriangle)
    rightTriangle.a = 3
    rightTriangle.b = 4
    rightTriangle.c = 5

    fmt.Println("(1) call struct method:")
    fmt.Println("square area is: ", square.Area())
    fmt.Println("rectangle area is: ", rectangle.Area())
    fmt.Println("right triangle area is: ", rightTriangle.Area())

    fmt.Println("\n(2) via interface:")
    var shape Shaper
    shape = square
    fmt.Println("square area is: ", shape.Area())
    shape = rectangle
    fmt.Println("rectangle area is: ", shape.Area())
    shape = rightTriangle
    fmt.Println("right triangle area is: ", shape.Area())

    fmt.Println("\n(3) via empty interface:")
    var anyShape AnyShape
    anyShape = square
    fmt.Println("square area is: ", anyShape.(*Square).Area())
    anyShape = rectangle
    fmt.Println("rectangle area is: ", anyShape.(*Rectangle).Area())
    anyShape = rightTriangle
    fmt.Println("right triangle area is: ", anyShape.(*RightTriangle).Area())

    fmt.Println("\n(4) type assertions via switch:")
    switch shape := anyShape.(type) {
    case *RightTriangle:
        fmt.Printf("shape type is: %T\n", shape)
        fmt.Println("rectangle area is: ", shape.Area())
    default:
        fmt.Printf("unknown type %T\n", shape)
    }

    fmt.Println("\n(5) type assertions via comma, ok pattern:")
    anyShape = rectangle
    if shape, ok := anyShape.(*Rectangle); ok {
        fmt.Printf("shape type is: %T\n", shape)
        fmt.Println("rectangle area is: ", shape.Area())
    } else {
        fmt.Printf("unknown type %T\n", shape)
    }
}

输出:

(1) call struct method:
square area is:  144
rectangle area is:  60
right triangle area is:  6

(2) via interface:
square area is:  144
rectangle area is:  60
right triangle area is:  6

(3) via empty interface:
square area is:  144
rectangle area is:  60
right triangle area is:  6

(4) type assertions via switch:
shape type is: *main.RightTriangle
rectangle area is:  6

(5) type assertions via comma, ok pattern:
shape type is: *main.Rectangle
rectangle area is:  60

参考

《the way to go》

LEo at 23:40

本文参与腾讯云自媒体分享计划,欢迎正在阅读的你也加入,一起分享。

发表于

我来说两句

0 条评论
登录 后参与评论

相关文章

来自专栏李蔚蓬的专栏

JavaScript_note1

4.6.字符串运算符 字符串运算符是用于两个字符型数据之间的运算符,除了比较运算符之外,还可以是+和+=运算符。 Demo:

972
来自专栏王磊的博客

vuejs深入浅出—基础篇

一、从HelloWorld说起 任何语言的都是从Hello World开始的,VueJs也不例外,直接上代码: <script src="https://unp...

3146
来自专栏C/C++基础

web前端开发初学者十问集锦(4)

利用JS来控制页面控件的显示和隐藏有两种方法,两种方法分别利用HTML的style中的两个属性,两种方法的不同之处在于控件隐藏后是否还在页面上占空位。

1602
来自专栏Python爱好者

Python高效编程(一)

2159
来自专栏nimomeng的自我进阶

《Objective-C基础教程》笔记

1.xcode中,oc的.m文件代表message,指的是Objective-C的一个主要特性。 2.NS前缀的来历要追溯到次公局包还被成为NextStep,...

792
来自专栏Pythonista

Python3编程技巧

Microsoft Excel是Microsoft为使用Windows和Apple Macintosh操作系统的计算机编写的一款电子表格软件。直观的界面、出色的...

1022
来自专栏Golang语言社区

Go 语言简介(上)— 语法

Hello World package main //声明本文件的package名 import "fmt" //import语言的fmt库——用于输出 f...

3978
来自专栏Golang语言社区

JS基础(下)

事件DOM Event 更多事件请查询h3c htmldom参考手册 event onclick() :当点击时 onfocus() :获得焦点时 ;当...

4287
来自专栏前端杂货铺

前端开发中的字符编码

前端开发过程中会接触各种各样的编码,比较常见的主要是UTF-8和HTML实体编码,但是web前端的世界却不止这两种编码,而且编码的选择也会造成一定的问题,如前后...

3288
来自专栏偏前端工程师的驿站

JS魔法堂:那些困扰你的DOM集合类型

一、前言                                     大家先看看下面的js,猜猜结果会怎样吧!   可选答案:   ①. 获取id属...

2229

扫码关注云+社区

领取腾讯云代金券