跟我一起来学golang之《接口》(一)

567 阅读5分钟

这是我参与8月更文挑战的第13天,活动详情查看:8月更文挑战

在Go中,接口是一组方法签名。当类型为接口中的所有方法提供定义时,它被称为实现接口。它与OOP非常相似。接口指定了类型应该具有的方法,类型决定了如何实现这些方法。

它把所有的具有共性的方法定义在一起,任何其他类型只要实现了这些方法就是实现了这个接口

接口定义了一组方法,如果某个对象实现了某个接口的所有方法,则此对象就实现了该接口。

接口的定义语法

/* 定义接口 */
type interface_name interface {
   method_name1 [return_type]
   method_name2 [return_type]
}

/* 定义结构体 */
type struct_name struct {
   /* variables */
}

/* 实现接口方法 */
func (struct_name_variable struct_name) method_name1() [return_type] {
   /* 方法实现 */
}
...
func (struct_name_variable struct_name) method_namen2() [return_type] {
   /* 方法实现*/
}

示例代码:

package main

import (
    "fmt"
)

type Phone interface {
    call()
}

type NokiaPhone struct {
}

func (nokiaPhone NokiaPhone) call() {
    fmt.Println("I am Nokia, I can call you!")
}

type IPhone struct {
}

func (iPhone IPhone) call() {
    fmt.Println("I am iPhone, I can call you!")
}

func main() {
    var phone Phone

    phone = new(NokiaPhone)
    phone.call()

    phone = new(IPhone)
    phone.call()

}

运行结果:

I am Nokia, I can call you!
I am iPhone, I can call you!

示例代码:

package main

import (
    "fmt"
)


type USB interface {
    Name() string
    PlugIn()
}

type FlashDisk struct {
    name string
}

func (fd FlashDisk)Name() string  {
    return fd.name
}

func (fd FlashDisk) PlugIn()  {
    fmt.Println(fd.name,"连入电脑中。。")
}

type Mouse struct {
    name string
}

func (m Mouse)Name() string  {
    return m.name
}

func (m Mouse) PlugIn()  {
    fmt.Println(m.name,"连入电脑,准备工作。。")
}

func main()  {
    /*
    接口:是一个方法或多个方法声明的集合
    只要某个类型拥有该接口的所有方法的声明,就算实现该接口。无需显示声明实现了哪个接口,
    这称为structural typing
    接口声明只有方法声明,没有方法实现,没有数据字段
     */
     fd := FlashDisk{"U盘"}
     fmt.Println(fd.Name())
     fd.PlugIn()

     m1:=Mouse{"鼠标"}
     fmt.Println(m1.Name())
     m1.PlugIn()
}

运行结果:

U盘
U盘 连入电脑中。。
鼠标
鼠标 连入电脑,准备工作。。
  • interface可以被任意的对象实现
  • 一个对象可以实现任意多个interface
  • 任意的类型都实现了空interface(我们这样定义:interface{}),也就是包含0个method的interface

interface值

示例代码:

package main

import "fmt"

type Human struct {
    name  string
    age   int
    phone string
}
type Student struct {
    Human  //匿名字段
    school string
    loan   float32
}
type Employee struct {
    Human   //匿名字段
    company string
    money   float32
} //Human实现Sayhi方法
func (h Human) SayHi() {
    fmt.Printf("Hi, I am %s you can call me on %s\n", h.name, h.phone)
} //Human实现Sing方法
func (h Human) Sing(lyrics string) {
    fmt.Println("La la la la...", lyrics)
} //Employee重写Human的SayHi方法
func (e Employee) SayHi() {
    fmt.Printf("Hi, I am %s, I work at %s. Call me on %s\n", e.name,
        e.company, e.phone) //Yes you can split into 2 lines here.
}

// Interface Men被Human,Student和Employee实现
// 因为这三个类型都实现了这两个方法
type Men interface {
    SayHi()
    Sing(lyrics string)
}

func main() {
    mike := Student{Human{"Mike", 25, "222-222-XXX"}, "MIT", 0.00}
    paul := Student{Human{"Paul", 26, "111-222-XXX"}, "Harvard", 100}
    sam := Employee{Human{"Sam", 36, "444-222-XXX"}, "Golang Inc.", 1000}
    Tom := Employee{Human{"Sam", 36, "444-222-XXX"}, "Things Ltd.", 5000}
    //定义Men类型的变量i
    var i Men
    //i能存储Student
    i = mike
    fmt.Println("This is Mike, a Student:")
    i.SayHi()
    i.Sing("November rain")
    //i也能存储Employee
    i = Tom
    fmt.Println("This is Tom, an Employee:")
    i.SayHi()
    i.Sing("Born to be wild")
    //定义了slice Men
    fmt.Println("Let's use a slice of Men and see what happens")
    x := make([]Men, 3)
    //T这三个都是不同类型的元素,但是他们实现了interface同一个接口
    x[0], x[1], x[2] = paul, sam, mike
    for _, value := range x {
        value.SayHi()
    }
}

那么interface里面到底能存什么值呢?如果我们定义了一个interface的变量,那么这个变量里面可以存实现这个interface的任意类型的对象。例如上面例子中,我们定义了一个Men interface类型的变量m,那么m里面可以存Human、Student或者Employee值

当然,使用指针的方式,也是可以的

但是,接口对象不能调用实现对象的属性

示例代码:

package main

import "fmt"

type Run interface{
    start()
    end()
}

type Runner struct {
    name string
}

type Human2 struct {
    Runner
    age int
}

type Pig struct{
    Runner
    Color string
}

func (r Runner) start() {
    fmt.Println(r.name,",开始跑。。")
}
func (r Runner) end()  {
    fmt.Println(r.name,"停止跑步。。")
}

// 方法重写
func (h Human2) start()  {
    fmt.Println(h.name, "今年",h.age,"岁了,跑步前需要先热身。。然后开始跑步。。")
}


func main()  {
    h1:=Human2{Runner{"张三"},30}
    p1:=Pig{Runner{"小猪佩奇"},"粉色"}
    h1.start()
    h1.end()
    p1.start()
    p1.end()

    fmt.Println("------------------")
    // 定义接口类型变量,可以指向实现类的对象。模拟多态
    var r1 Run
    r1 = h1
    r1.start()
    r1.end()
    fmt.Println(h1.age)
    //fmt.Println(r1.age) // 接口类型对象,不能访问具体的子类对象的字段

    fmt.Printf("%T,%T\n",h1,r1) // main.Human2,main.Human2

    var r2 Run
    r2 = p1
    r2.start()
    r2.end()


    fmt.Println("-------------")
    // 创建一个数组:定义为接口类型,但实际上可以存入任意的子类对象
    arr := [2]Run{h1,p1}
    for _,v := range  arr{
        v.start()
        v.end()
    }

    testRun(p1)
    testRun(r1)

}

func testRun(r Run)  { // 定义一个函数,接收一个借口类型的对象作为参数。
    fmt.Println("测试赛跑者。。。")
    r.start()
    r.end()
}

运行结果:

张三 今年 30 岁了,跑步前需要先热身。。然后开始跑步。。
张三 停止跑步。。
小猪佩奇 ,开始跑。。
小猪佩奇 停止跑步。。
------------------
张三 今年 30 岁了,跑步前需要先热身。。然后开始跑步。。
张三 停止跑步。。
30
main.Human2,main.Human2
小猪佩奇 ,开始跑。。
小猪佩奇 停止跑步。。
-------------
张三 今年 30 岁了,跑步前需要先热身。。然后开始跑步。。
张三 停止跑步。。
小猪佩奇 ,开始跑。。
小猪佩奇 停止跑步。。
测试赛跑者。。。
小猪佩奇 ,开始跑。。
小猪佩奇 停止跑步。。
测试赛跑者。。。
张三 今年 30 岁了,跑步前需要先热身。。然后开始跑步。。
张三 停止跑步。。

interface函数参数

interface的变量可以持有任意实现该interface类型的对象,这给我们编写函数(包括method)提供了一些额外的思考,我们是不是可以通过定义interface参数,让函数接受各种类型的参数。

package main

import (
    "fmt"
)

type sharp interface {
    area() float64
}

type sqrt struct{
    l float64
}

func (s sqrt) area() float64 {
    return s.l * s.l
}

type circle struct{
    r float64
}

func (c circle)area() float64  {
    return c.r * 3.14 * c.r
}


// 接口类型作为参数
func getArea(s sharp)  {
    fmt.Println(s.area())
}


func main()  {
    /*
    接口作为参数
     */
     s1:=sqrt{6.5}
     c1:=circle{2.5}

     getArea(s1)
     getArea(c1)


}

运行结果:

42.25
19.625