我们知道interface的变量里面可以存储任意类型的数值(该类型实现了interface)。
那么我们怎么反向知道这个变量里面实际保存了的是哪个类型的对象呢?
目前常用的有两种方法:
Comma-ok断言
Go语言里面有一个语法,可以直接判断是否是该类型的变量:
value, ok = element.(T)
这里value
就是变量的值,ok
是一个bool
类型,element
是interface
变量,T
是断言的类型。
如果element
里面确实存储了T
类型的数值,那么ok
返回true
,否则返回false
package main
import "fmt"
type Student struct {
name string
id int
}
func main() {
i := make([]interface{}, 3) // 定义 interface 数组
i[0] = 1 //int
i[1] = "hello go" //string
i[2] = Student{"mike", 666} //Student
//类型查询,类型断言
//第一个返回下标,第二个返回下标对应的值,data分别是i[0],i[1],i[2]
for index, data := range i{
// 第一个返回的是值,第二个返回判断结果的真假
if value, ok := data.(int); ok == true {
fmt.Printf("x[%d] 类型为int, 内容为%s\n", index, value)
} else if value, ok := data.(string); ok == true {
fmt.Printf("x[%d] 类型为string, 内容为%s\n", index, value)
} else if value, ok := data.(Student); ok == true {
fmt.Printf("x[%d] 类型为Student, 内容为%s\n", index, value.name)
}
}
}
执行如下:
x[0] 类型为int, 内容为%!s(int=1)
x[1] 类型为string, 内容为hello go
x[2] 类型为Student, 内容为mike
将上面案例中的 if-else 判断改为 switch 方式:
package main
import "fmt"
type Student struct {
name string
id int
}
func main() {
i := make([]interface{}, 3) // 定义 interface 数组
i[0] = 1 //int
i[1] = "hello go" //string
i[2] = Student{"mike", 666} //Student
//类型查询,类型断言
//第一个返回下标,第二个返回下标对应的值,data分别是i[0],i[1],i[2]
for index, data := range i{
switch value := data.(type) {
case int:
fmt.Printf("x[%d] 类型为int, 内容为%d\n", index, value)
case string:
fmt.Printf("x[%d] 类型为string, 内容为%s\n", index, value)
case Student:
fmt.Printf("x[%d] 类型为Student, 内容为%s\n", index, value.name)
}
}
}
现在我们已经将空接口与类型断言的基本语法给大家讲解完毕了,那么在实际的开发中,我们应该怎样应用这方面的知识呢?下面我们将前面我们写的计算器这个案例,结合空接口与类型断言,在给大家写一遍。
具体的实现如下:
// 操作父类
type Operation struct {
numA float64
numB float64
}
// 加法类,继承父类
type Add struct {
Operation
}
// 减法类,继承父类
type Subtraction struct {
Operation
}
现在父类已经定义完成,并且定义了加法类与减法类,继承父类
// 定义计算器的接口
type CalcSuper interface {
SetData(data ...interface{}) // 验证数据
CalcOperate() float64
}
这个接口的定义与我们前面定义的接口不同之处,就是在这里我们又加入了一个方法SetData( ),该方法的作用主要是对传递过来的数据进行校验,例如,我们要求对float64类型的数据进行运算,那么只能传递小数,如果传递过来的是int类型,那么会给出相应的错误提示。该方法的参数是:不定参数,同时也是空接口,表示可以传递各种类型的数据。
以下是加法类实现对应的接口中声明的方法。
实现SetData( )方法
// 加法类的数据校验
func (a *Add) SetData(data ...interface{}) {
if len(data) != 2 {
fmt.Println("error,Need two parameters")
return
}
if _,ok := data[0].(float64); !ok{
fmt.Println("error,Need float64 parameters")
return
}
if _,ok := data[1].(float64); !ok{
fmt.Println("error,Need float64 parameters")
return
}
a.numA, _ = data[0].(float64)
a.numB, _ = data[0].(float64)
}
在改方法中首先对传递过来的数据的长度进行校验,然后对类型进行校验。
实现CalcOperate( )方法
// 实现加法类的加法
func (a *Add) CalcOperate() float64{
return a.numA + a.numB
}
同理减法类的实现如下:
// 减法类的数据校验
func (a *Subtraction) SetData(data ...interface{}) {
if len(data) != 2 {
fmt.Println("error,Need two parameters")
return
}
if _,ok := data[0].(float64); !ok{
fmt.Println("error,Need float64 parameters")
return
}
if _,ok := data[1].(float64); !ok{
fmt.Println("error,Need float64 parameters")
return
}
a.numA, _ = data[0].(float64)
a.numB, _ = data[0].(float64)
}
// 实现加法类的加法
func (a *Subtraction) CalcOperate() float64{
return a.numA - a.numB
}
为了在main( )函数中,更方面的创建加法类对象,与减法类对象,所以将对象的创建封装一下。
为了解决这个问题,我们前面定义了一个OperationFactory类(结构体),并且为该类创建了一个CreateOption( )方法,该方法完成对象创建,并且该方法返回的类型是一个float64,表示的运算结果。但是,如果我想返回一个对象,应该怎样做呢?可以将返回的类型改成接口类型,因为加法类与减法类都实现了该接口,所以定义如下:
// 计算工厂类
type CalcFactory struct {
}
func (f *CalcFactory) CreateOperate(opType string) CalcSuper {
}
该方法主要是根据传递过来的参数opType进行判断,创建出不同的对象。
为了不让改方法代码量过于庞大,过于复杂,我们将对象的创建又单独的放在了不同的方法中。如下所示:
// 创建Add对象,返回指针类型
func NewAdd() *Add {
instance := new(Add)
return instance
}
// 创建Subtraction对象,返回指针类型
func NewSubtraction() *Subtraction {
instance := new(Subtraction)
return instance
}
接下来在CreateOperate( )方法中完成以上两个方法的调用,具体如下所示:
func (f *CalcFactory) CreateOperate(opType string) CalcSuper {
var op CalcSuper
switch opType {
case "+":
op = NewAdd()
case "-":
op = NewSubtraction()
default:
panic("error! don't has this operate")
}
return op
}
(1) 首先完成CalcFactory类对象的创建,也是单独封装在一个方法中。
// CalcFactory对象的创建
func NewCalcFactory() *CalcFactory {
instance := new(CalcFactory)
return instance
}
(2) 在main( )函数中完成NewCalcFactory( )方法的调用,获取CalcFactory的对象
// 获取工厂
factory := NewCalcFactory()
(3) 完成后续方法的调用
op := factory.CreateOperate("+")
op.SetData(1.5, 2.0)
fmt.Println(op.CalcOperate())
op = factory.CreateOperate("-")
op.SetData(1.5, 2.0)
fmt.Println(op.CalcOperate())
在这个程序中,大家要体会总结出与前面程序的不同之处。
package main
import "fmt"
// 操作父类
type Operation struct {
numA float64
numB float64
}
// 加法类,继承父类
type Add struct {
Operation
}
// 创建Add对象,返回指针类型
func NewAdd() *Add {
instance := new(Add)
return instance
}
// 加法类的数据校验
func (a *Add) SetData(data ...interface{}) {
if len(data) != 2 {
fmt.Println("error,Need two parameters")
return
}
if _, ok := data[0].(float64); !ok {
fmt.Println("error,Need float64 parameters")
return
}
if _, ok := data[1].(float64); !ok {
fmt.Println("error,Need float64 parameters")
return
}
a.numA, _ = data[0].(float64)
a.numB, _ = data[0].(float64)
}
// 实现加法类的加法
func (a *Add) CalcOperate() float64 {
return a.numA + a.numB
}
// 减法类,继承父类
type Subtraction struct {
Operation
}
// 创建Subtraction对象,返回指针类型
func NewSubtraction() *Subtraction {
instance := new(Subtraction)
return instance
}
// 减法类的数据校验
func (a *Subtraction) SetData(data ...interface{}) {
if len(data) != 2 {
fmt.Println("error,Need two parameters")
return
}
if _, ok := data[0].(float64); !ok {
fmt.Println("error,Need float64 parameters")
return
}
if _, ok := data[1].(float64); !ok {
fmt.Println("error,Need float64 parameters")
return
}
a.numA, _ = data[0].(float64)
a.numB, _ = data[0].(float64)
}
// 实现减法类的减法
func (a *Subtraction) CalcOperate() float64 {
return a.numA - a.numB
}
// 定义计算器的接口
type CalcSuper interface {
SetData(data ...interface{}) // 验证数据
CalcOperate() float64
}
// 计算工厂类
type CalcFactory struct {
}
// CalcFactory对象的创建
func NewCalcFactory() *CalcFactory {
instance := new(CalcFactory)
return instance
}
// 使用工厂类创建减法、减法的对象
func (f *CalcFactory) CreateOperate(opType string) CalcSuper {
var op CalcSuper
switch opType {
case "+":
op = NewAdd()
case "-":
op = NewSubtraction()
default:
panic("error! don't has this operate")
}
return op
}
func main() {
// 获取工厂
factory := NewCalcFactory()
op := factory.CreateOperate("+")
op.SetData(1.5, 2.0)
fmt.Println(op.CalcOperate())
op = factory.CreateOperate("-")
op.SetData(1.5, 2.0)
fmt.Println(op.CalcOperate())
}
在面向对象的编程中,重点理解面向对象编程思想,同时能够理解继承,封装和多态的应用。