Golang 学习笔记Golang与区块链

Golang:接口

2018-01-15  本文已影响43人  与蟒唯舞
什么是接口

在 Golang 中,一个接口是一组方法签名。当一个类型定义了接口里所有定义的方法时,就说这个类型实现了这个接口。接口指定类型应该具有的方法,类型决定如何实现这些方法。

声明和实现一个接口
package main

import (  
    "fmt"
)

//interface definition
type VowelsFinder interface {  
    FindVowels() []rune
}

type MyString string

//MyString implements VowelsFinder
func (ms MyString) FindVowels() []rune {  
    var vowels []rune
    for _, rune := range ms {
        if rune == 'a' || rune == 'e' || rune == 'i' || rune == 'o' || rune == 'u' {
            vowels = append(vowels, rune)
        }
    }
    return vowels
}

func main() {  
    name := MyString("Sam Anderson")
    var v VowelsFinder
    v = name // possible since MyString implements VowelsFinder
    fmt.Printf("Vowels are %c", v.FindVowels())

}

在上面的程序中,创建了一个接口类型名为 VowelsFinder,它有一个方法 FindVowels() []rune

通过添加一个方法 FindVowels() []rune 给接受者类型 MyString。现在可以说 MyString 实现了 VowelsFinder 接口。在 Golang 中,如果一个类型包含了一个接口声明的所有方法,那么这个类型就隐式地实现了这个接口。

MyString 类型的变量 name 赋值给 VowelsFinder 类型的变量 v。这是合法的,因为 MyString 实现了 VowelsFinderv.FindVowels()MyString 上调用 FindVowels 方法打印字符串 Sam Anderson 中所有的元音。

空接口

一个没有声明任何方法的接口称为空接口。空接口表示为 interface{}。因为空接口没有方法,因此所有类型都实现了空接口。

package main

import (  
    "fmt"
)

func describe(i interface{}) {  
    fmt.Printf("Type = %T, value = %v\n", i, i)
}

func main() {  
    s := "Hello World"
    describe(s)
    i := 55
    describe(i)
    strt := struct {
        name string
    }{
        name: "Naveen R",
    }
    describe(strt)
}

上面的程序,describe(i interface{}) 函数接受一个空接口作为参数,因此任何值都可以传递给它。

接口内部表示

一个接口可以被认为是由一个元组内部表示的 (type, value)type 是接口的具体类型,value 是具体类型的值。

package main

import (  
    "fmt"
)

type Test interface {  
    Tester()
}

type MyFloat float64

func (m MyFloat) Tester() {  
    fmt.Println(m)
}

func describe(t Test) {  
    fmt.Printf("Interface type %T value %v\n", t, t)
}

func main() {  
    var t Test
    f := MyFloat(89.7)
    t = f
    describe(t)
    t.Tester()
}

Test 接口提供了一个方法 Tester()MyFloat 类型实现了这个接口。将 MyFloat 类型的变量 f 赋值给 Test 类型的变量 t 。现在 t 的具体类型是 MyFloat 而它的值是 89.7。

接口的零值

接口的零值是 nil。一个 nil 接口的底层类型和值都是 nil

package main

import "fmt"

type Describer interface {  
    Describe()
}

func main() {  
    var d1 Describer
    if d1 == nil {
        fmt.Printf("d1 is nil and has type %T value %v\n", d1, d1)
    }
}

如果试图在一个 nil 接口上调用方法,程序将会触发 panic,因为 nil 接口既没有底层的值,也没有具体的类型。

package main

type Describer interface {  
    Describe()
}

func main() {  
    var d1 Describer
    d1.Describe()
}

上面的程序,因为 d1nil。程序将在运行时触发 panicruntime error: invalid memory address or nil pointer dereference

实现多个接口

一个类型可以实现多个接口。

package main

import (  
    "fmt"
)

type SalaryCalculator interface {  
    DisplaySalary()
}

type LeaveCalculator interface {  
    CalculateLeavesLeft() int
}

type Employee struct {  
    firstName string
    lastName string
    basicPay int
    pf int
    totalLeaves int
    leavesTaken int
}

func (e Employee) DisplaySalary() {  
    fmt.Printf("%s %s has salary $%d", e.firstName, e.lastName, (e.basicPay + e.pf))
}

func (e Employee) CalculateLeavesLeft() int {  
    return e.totalLeaves - e.leavesTaken
}

func main() {  
    e := Employee {
        firstName: "Naveen",
        lastName: "Ramanathan",
        basicPay: 5000,
        pf: 200,
        totalLeaves: 30,
        leavesTaken: 5,
    }
    var s SalaryCalculator = e
    s.DisplaySalary()
    var l LeaveCalculator = e
    fmt.Println("\nLeaves left =", l.CalculateLeavesLeft())
}
嵌入接口

尽管 Golang 不提供继承,但是可以通过嵌入其他接口来创建新的接口。

package main

import (  
    "fmt"
)

type SalaryCalculator interface {  
    DisplaySalary()
}

type LeaveCalculator interface {  
    CalculateLeavesLeft() int
}

type EmployeeOperations interface {  
    SalaryCalculator
    LeaveCalculator
}

type Employee struct {  
    firstName string
    lastName string
    basicPay int
    pf int
    totalLeaves int
    leavesTaken int
}

func (e Employee) DisplaySalary() {  
    fmt.Printf("%s %s has salary $%d", e.firstName, e.lastName, (e.basicPay + e.pf))
}

func (e Employee) CalculateLeavesLeft() int {  
    return e.totalLeaves - e.leavesTaken
}

func main() {  
    e := Employee {
        firstName: "Naveen",
        lastName: "Ramanathan",
        basicPay: 5000,
        pf: 200,
        totalLeaves: 30,
        leavesTaken: 5,
    }
    var empOp EmployeeOperations = e
    empOp.DisplaySalary()
    fmt.Println("\nLeaves left =", empOp.CalculateLeavesLeft())
}
类型断言

类型断言用来提取接口的实际类型的值。

语法:i.(T),用来获取接口 i 的实际类型 T 的值。

package main

import (  
    "fmt"
)

func assert(i interface{}) {  
    s := i.(int) //get the underlying int value from i
    fmt.Println(s)
}
func main() {  
    var s interface{} = 56
    assert(s)
}

变量 s 的实际类型是 int。使用 i.(int) 来获取 iint 值。

如果实际类型不是 int,那么上面的程序会发生什么?

package main

import (  
    "fmt"
)

func assert(i interface{}) {  
    s := i.(int) 
    fmt.Println(s)
}
func main() {  
    var s interface{} = "Steven Paul"
    assert(s)
}

在上面的程序中,将实际类型为 string 的变量 s 传递给 assert 函数,assert 函数尝试从其中提取出一个 int 值。该程序会触发 panicinterface conversion: interface {} is string, not int

为了解决以上问题,可以使用下面的语法:

v, ok := i.(T)  

如果接口 i 的具体类型是 T,则 v 将具有 i 的实际值,ok 为 true。

如果接口 i 的具体类型不是 T,则 ok 为 false, vT 的零值,但程序不会触发 panic

package main

import (  
    "fmt"
)

func assert(i interface{}) {  
    v, ok := i.(int)
    fmt.Println(v, ok)
}
func main() {  
    var s interface{} = 56
    assert(s)
    var i interface{} = "Steven Paul"
    assert(i)
}

当把字符串 Steven Paul 传递给 assert 函数,ok 将是 false,因为 i 的实际类型不是 intv 的值将是 0(int 的零值)。

Type Switch

类型分支(type switch)用来将一个接口的具体类型与多个 case 语句指定的类型进行比较。在类型断言 i.(T) 中,将类型 T 替换为关键字 type 就变成了 type switch。

package main

import (  
    "fmt"
)

func findType(i interface{}) {  
    switch i.(type) {
    case string:
        fmt.Printf("I am a string and my value is %s\n", i.(string))
    case int:
        fmt.Printf("I am an int and my value is %d\n", i.(int))
    default:
        fmt.Printf("Unknown type\n")
    }
}
func main() {  
    findType("Naveen")
    findType(77)
    findType(89.98)
}

也可以将类型与接口进行比较。如果我们有一个类型,并且如果这个类型实现了一个接口,就可以将这个类型与它实现的接口进行比较。

package main

import "fmt"

type Describer interface {  
    Describe()
}
type Person struct {  
    name string
    age  int
}

func (p Person) Describe() {  
    fmt.Printf("%s is %d years old", p.name, p.age)
}

func findType(i interface{}) {  
    switch v := i.(type) {
    case Describer:
        v.Describe()
    default:
        fmt.Printf("unknown type\n")
    }
}

func main() {  
    findType("Naveen")
    p := Person{
        name: "Naveen R",
        age:  25,
    }
    findType(p)
}
上一篇下一篇

猜你喜欢

热点阅读