面向对象编程三大特征7

多态及体现:

基本介绍:

  变量(实例)具有多种形态。面向对象的第三大特征,在Go语言,多态特征是通过接口实现的。可以按照统一的接口来调用不同的实现。这时接口变量就呈现不同的形态。


快速入门:

在前面的Usb接口案例, Usb usb,既可以接收手机变量,又可以接收相机变量,就体现了Usb 接口多态特征。

//编写一个方法Working 这个方法接收Usb接口类型的变量
//只要是实现了 Usb接口 (所谓实现Usb接口就是指实现了Usb接口声明的所有方法)
func (c Computer) Working(usb Usb) {   //usb 变量会根据传入的实参,来判断到底是Phone,还是Camera,usb 接口变量就体现出多态的特点。
  //通过usb接口变量来调用Start和Stop方法
  usb.Start()
  usb.Stop()
}

 

接口体现多态特征:

1)多态参数

  在前面的Usb接口案例,usb Usb,既可以接收手机变量,又可以接收相机变量,就体现了Usb 接口多态特征。

2)多态数组

  演示一个案例:给Usb数组中,存放Phone 结构体 和 Camera 结构体变量,Phone还有一个特有的方法call(),请遍历Usb数组,如果是Phone变量,除了调用Usb 接口声明的方法外,还需要调用Phone 特有方法 call。

蓝色部分讲完类型断言之后再讲特有的调用方法。

案例说明:

//声明一个接口
type Usb interface {
  //声明了两个没有实现的方法
  Start()
  Stop()
}

type Phone struct {
  name string
}

//让Phone 实现 Usb接口的方法
func (p Phone) Start() {
  fmt.Println("手机开始工作...")
}

func (p Phone) Stop() {
  fmt.Println("手机停止工作...")
}

type Camera struct {
  name string
}

//让camera 实现了Usb接口的方法
func (c Camera) Start() {
  fmt.Println("相机开始工作...")
}

func (c Camera) Stop() {
  fmt.Println("相机停止工作...")
}

func main() {

  //定义一个usb接口数组,可以存放Phone和Camera结构体变量
  //这里就体现多态数组
  var usbArr [3]Usb
  usbArr[0] = Phone{"vivo"}
  usbArr[1] = Phone{"小米"}
  usbArr[2] = Camera{"三星"}
  fmt.Println(usbArr)
}

 

类型断言:

先看一个需求:

看一段代码
type Point struct {
  x int
  y int
}

func main() {
  var a interface{}
  var point Point = Point{1,2}
  a = point   //ok
  //如何将a赋给一个Point变量?
  var b Point
  b = a   //可以吗? ==>error 必须是 b = a.(Point) 类型断言
  fmt.Println(b)
}

b = a.(Point) 就是类型断言,表示判断a是否指向Point类型的变量,如果是就转成Point类型并赋给b变量,否则报错。

需求:如何将一个接口变量,赋给自定义类型的变量. => 引出类型断言


基本介绍:

类型断言,由于接口是一般类型,不知道具体类型,如果要转成具体类型,就需要使用类型断言,具体的如下:


案例:

func main() {
  var x interface {}
  var b float32 = 1.1
  x = b //空接口,可以接收任意的类型
  // x=>float32 [使用类型断言]
  y := x.(float32)
  fmt.Printf("y 的类型是 %T 值是%v", y, y)
}


对上面的代码说明:

在进行类型断言时,如果类型不匹配,就会报 panic,因此进行类型断言时,要确保原来的空接口指向的就是断言的类型。

比如 y := x.(float32) 写成float64就不行了,因为空接口指向的是float32类型。


如何在进行断言时,带上检测机制,如果成功就OK,否则也不需要报 panic。

带检测的类型断言:

func main() {
  var x interface {}
  var b float32 = 2.1
  x = b //空接口,可以接收任意的类型
  // x=>float32 [使用类型断言]

  //类型断言(带检测的)
  //y, flag := x.(float32)
  if y, flag := x.(float32); flag {
    fmt.Println("convert success")
    fmt.Printf("y 的类型是 %T 值是%v", y, y)
  } else {
    fmt.Println("convert fail")
  }
  fmt.Println("继续执行~~~")
}


类型断言的最佳实践1:

在前面的Usb接口案例做改进:

  给Phone 结构体增加一个特有的方法call(),当Usb 接口接收的是Phone 变量时,还需要调用call方法。

//声明一个接口
type Usb interface {
  //声明了两个没有实现的方法
  Start()
  Stop()
}

type Phone struct {
  name string
}

//让Phone 实现 Usb接口的方法
func (p Phone) Start() {
  fmt.Println("手机开始工作...")
}

func (p Phone) Stop() {
  fmt.Println("手机停止工作...")
}

func (p Phone) Call() {
  fmt.Println("手机 在打电话..")
}

type Camera struct {
  name string
}

//让camera 实现了Usb接口的方法
func (c Camera) Start() {
  fmt.Println("相机开始工作...")
}

func (c Camera) Stop() {
  fmt.Println("相机停止工作...")
}

type Computer struct {

}

func (computer Computer) Working(usb Usb) {
  usb.Start()
  //如果usb是指向Phone结构体变量,则还需要调用Call方法。
  //类型断言...[注意体会!!!]
  if a, ok := usb.(Phone); ok {
    a.Call()
  }
  usb.Stop()
}

func main() {

  //定义一个usb接口数组,可以存放Phone和Camera结构体变量
  //这里就体现多态数组
  var usbArr [3]Usb
  usbArr[0] = Phone{"vivo"}
  usbArr[1] = Phone{"小米"}
  usbArr[2] = Camera{"三星"}

  //遍历usbArr
  var computer Computer
  for _, v := range usbArr{
    computer.Working(v)
    fmt.Println()
  }
  fmt.Println(usbArr)
}


类型断言的最佳实践2:

//编写一个函数,可以判断输入的参数是什么类型
func TypeJudge(items... interface{}) {    //表示这个函数可以接收多个的任意类型的实参
  for i, x := range items {
    switch x.(type) { //这里type是一个关键字,固定写法
      case bool:
        fmt.Printf("第%d个参数是 bool 类型,值是%v \n", i+1, x)
      case float32:
        fmt.Printf("第%d个参数是 float32 类型,值是%v \n", i+1, x)
      case float64:
        fmt.Printf("第%d个参数是 float64 类型,值是%v \n", i+1, x)
      case int, int32, int64:
        fmt.Printf("第%d个参数是 整数 类型,值是%v \n", i+1, x)
      case string:
        fmt.Printf("第%d个参数是 string 类型,值是%v \n", i+1, x)
      default:
        fmt.Printf("第%d个参数是 类型 不确定,值是%v \n", i+1, x)
    }
  }
}

func main() {

  var n1 float32 = 1.1
  var n2 float64 = 2.3
  var n3 int64 = 30
  var name string = "tom"
  address := "北京"
  n4 := 300

  TypeJudge(n1, n2, n3, name, address, n4)
}

结果是:

类型断言的最佳实践3:

在前面代码的基础上,增加判断Student 类型和 *Student类型

package main
import (
  "fmt"

)

type Student struct {

}

//编写一个函数,可以判断输入的参数是什么类型
func TypeJudge(items... interface{}) {
  for i, x := range items {
    switch x.(type) { //这里type是一个关键字,固定写法
      case bool:
        fmt.Printf("第%d个参数是 bool 类型,值是%v \n", i+1, x)
      case float32:
        fmt.Printf("第%d个参数是 float32 类型,值是%v \n", i+1, x)
      case float64:
        fmt.Printf("第%d个参数是 float64 类型,值是%v \n", i+1, x)
      case int, int32, int64:
        fmt.Printf("第%d个参数是 整数 类型,值是%v \n", i+1, x)
      case string:
        fmt.Printf("第%d个参数是 string 类型,值是%v \n", i+1, x)
      case Student :
        fmt.Printf("第%d个参数是 Student 类型,值是%v \n", i+1, x)
      case *Student :
        fmt.Printf("第%d个参数是 *Student 类型,值是%v \n", i+1, x)
      default:
        fmt.Printf("第%d个参数是 类型 不确定,值是%v \n", i+1, x)
    }
  }
}

func main() {

  var n1 float32 = 1.1
  var n2 float64 = 2.3
  var n3 int64 = 30
  var name string = "tom"
  address := "北京"
  n4 := 300

  stu1 := Student{}
  stu2 := &Student{}

  TypeJudge(n1, n2, n3, name, address, n4, stu1, stu2)
}

posted @ 2019-08-26 22:43  我是一只忙碌的小青蛙  阅读(269)  评论(0编辑  收藏  举报