【Go语言】【14】GO语言的接口类型

1、什么是接口?

在面向对象的语言中,接口是用来限制实现类行为的。怎么理解这句话呢?

定义一个Person接口,我只会站在我的角度上考虑问题,比如Person(人),自然想到会吃饭、睡觉等:


interface Person

{

// 人会吃饭

void eat();

// 人会睡觉

void sleep();

}

我是站在接口角度上考虑接口如何定义,此时不会过多考虑实现类的行为。

这很正常,因为我不能确定谁会使用我的接口,有一天SuperMan说:“我要用你定义的接口”,那SuperMan必须用implements实现Person接口的行为:


// SuperMan实现Person接口

public class SuperMan implements Person

{

// 超人会吃饭

public void eat()

{

System.out.println("super man can eat.");

}

// 超人会睡觉

public void sleep()

{

System.out.println("super man can sleep.");

}

}

等到SuperMan实现完了之后,他对我说:“作为超人,我是会飞的哦~”

这时作为Person定义者的我,只有两个选择:

  • 对SuperMan说:“飞是你自己的行为,我不帮你定义这种行为!”。可是经过若干万年之后人类进化了怎么办?
  • 对SuperMan说:“好吧,我帮你定义这种行为,可是一旦Person有了fly,你也必须实现fly”

其实无论上面哪种结果,都相当于接口把实现类绑架了。



【备注】:悄悄地告诉你,上面的代码是Java语言



2、GO语言的接口呢?

GO语言有接口类型(interface{}),它与面向对象的接口含义不同,GO语言的接口类型与数组(array)、切片(slice)、集合(map)、结构体(struct)是同等地位的。怎么理解这句话呢?

我们前面已知道:

var num int  // 定义了一个int型变量num

同理:

var any interface{} // 定义了一个接口类型变量any

从这个角度上看,GO的interface{}与面向对象的接口是不一样吧。   更加不一样的是,interface{}是一个任意类型,或者说是万能类型。

3、GO语言的任意类型

也就是说定义一个变量为interface{}类型,可以把任意的值赋给这个变量,例如:


var v1 interface{} = 250       // 把int值赋给interface{}

var v2 interface{} = "eagle" // 把string值赋给interface{}

var v3 interface{} = &v1      // 把v1的地址赋给interface{}

当然函数的入参类型也可以是interface{},这样函数就可以接受任意类型的参数,例如GO语言标准库fmt中的函数Println()


func Println(args ...interface{}){

// 略

}

任意类型看起来很爽,可以把任意的值都赋给interface{}类型变量,就像JDK1.4时的Vector,那时候Java还没有泛型的概念,任意值都可以向Vector里面放,但问题也接踵而至:


// 定义一个长度为3的Any类型数组,求数组元素之和,即anyArr[0]+anyArr[1]+anyArr[2]

var anyArr [3]interface{}

anyArr[0] = "eagle"    // anyArr[0]赋值为字符串

anyArr[1] = 20           // anyArr[1]赋值为int

anyArr[2] = 75.3        // anyArr[2]赋值为float64

// 此时若求和,会有什么结果呢?

fmt.Println(anyArr[0] + anyArr[1] + anyArr[2])

4、类型判断

上例直观上来看,string不能和int直接相加,所以我们需要判断元素类型,若元素类型是数字型的,我们就执行“+”操作;若元素类型是字符串型的,我们就跳过。这里需要引入另外一个知识:switch-type

即:拿到一个interface{}之后,可以结合switch语句判断变量的类型

例如:


var v interface{} = 3

switch v.(type){

case int:

fmt.Println("3 is int")

case string:

fmt.Println("3 is string")

default:

fmt.Println("unkown type")

}

所以上面的例子可以进一步修改如下:


// 定义一个长度为3的Any类型数组,求数组元素之和,即anyArr[0]+anyArr[1]+anyArr[2]

var anyArr [3]interface{}

anyArr[0] = "eagle"

anyArr[1] = 20

anyArr[2] = 75.3

// 定义一个总和变量total

var total float64 = 0

// 遍历Any类型数组

for i := 0; i < len(anyArr); i++ {

// 针对Any类型数组中的每个元素进行类型查询

switch vType := anyArr[i].(type) {

case int:

total += float64(vType)

case float64:

total += vType

default:

// do nothing

}

}

// 打印Any类型数组中数字之和

fmt.Println(total)

5、interface类型与struct类型

从上面看interface类型很简单嘛,或许吧。

我们再回顾一下struct类型,struct类型是一个结构体,里面可以定义成员,它类似面向对象的一个类,类里面可以定义成员变量,比如:


// 定义一个person结构体,里面有姓名、年龄、身高、体重成员

type person struct{

name                 string

age                    int

height, weight   float64

}

那么interface类型是否也可以这样定义呢?如下:


/**

* 定义一个手表接口,通过手表接口我们可以知道小时、分钟和秒

*/

type watch interface {

getHour() int

getMinute() int

getSecond() int

}

通过编译(go build myIf.go)会发现并没有抛出错误!

从结果可以看出完全可以这样定义一个类型为interface的变量watch,并且还可以为watch增加相应的方法;但与struct不同的是:struct里面的成员是变量,而interface里面的成员是函数,即我们可以使用interface定义接口。

6、interface定义接口示例

(1)GO语言接口实现

周围的不少朋友现在都有一款iWatch智能手表,一般都用来运动时监控心率,这也意味着iWatch不仅能看时间这么简单。下面我们定义一个iWatch类型:

type iWatch int    // 定义一个iWatch类型,它实际上就是int型;相当于为int型取一个别名iWatch

接下来为iWatch类型增加三个方法,分别为getHour()、getMinute()、getSecond()

// 为iWatch增加getHour()方法

func (w iWatch) getHour() int {

return time.Now().Hour()

}

// 为iWatch增加getMinute()方法

func (w iWatch) getMinute() int {

return time.Now().Minute()

}

// 为iWatch增加getSecond()方法

func (w iWatch) getSecond() int {

return time.Now().Second()

}

下面是GO语言的精彩内容,请各位看客睁大眼睛:


func main() {

var w watch  // 定义类型为watch的变量w

var t iWatch  // 定义类型为iWatch的变量t

w = t             // 把类型为watch的变量w赋值给类型为iWatch的变量t,这样能行的通吗?

fmt.Println("Current Hour:", w.getHour(), ", Minute:", w.getMinute(), ", Second:", w.getSecond())

}

在这个测试代码中:

var w watch

相当于定义了一个接口变量

var t iWatch

相当于定义了一个iWatch对象

w = t

直接把对象t 赋给了接口变量w,但没有像其它面向对象语言那样,让iWatch implements watch,这样能行的通吗?

把“吗”字去掉,请看结果:

神奇吧 :)

以上是GO语言的接口实现。

(2)Java语言的接口实现

用面向对象的编程语言来解释:

在面向对象的编程语言中,比如Son是一个实现类,Father是一个接口,Son要实现Father接口,必须使用implements显式地声明,同时在Son中实现Father里面定义的方法,比如:

interface Father{

getHour();

}

class Son implements Father{

// 实现父接口Father定义的方法getHour()

public int getHour(){

return 20;

}

}

一旦接口Father增加一个方法getSecond(),那么实现该接口的所有孩儿都必须实现getSecond()方法。在使用时:

Father father = new Son();

即孩儿对象可以赋值给Father接口



【备注】:若对上面面向对象编程语言不熟悉的话,建议看一下设计模式相关的书籍



(3)侵入式接口和非侵入式接口

像上面(2)中的Java就是侵入式接口。

GO语言中,像上面(1)所示,尽管定义了接口watch,但实现类iWatch并没有显示地声明实现该接口,只是watch中的方法都已在iWatch中实现,那么这种父子关系已建立,这种接口被称为“非侵入式接口”

7、引申

上面例子中,为iWatch定义了三个方法,现在我们修改一下这三个方法:

// 为*iWatch增加getHour()方法

func (w *iWatch) getHour() int {

return time.Now().Hour()

}

// 为*iWatch增加getMinute()方法

func (w *iWatch) getMinute() int {

return time.Now().Minute()

}

// 为*iWatch增加getSecond()方法

func (w *iWatch) getSecond() int {

return time.Now().Second()

}

这相当于并不是为iWatch类型增加了三个方法,而是为*iWatch类型增加了三个方法,那么调用时也需要相应修改:


func main() {

var w watch

var t iWatch

w = &t

fmt.Println("Current Hour:", w.getHour(), ", Minute:", w.getMinute(), ", Second:", w.getSecond())

}

好了,关于GO语言的接口类型就聊到这里,请记住这么三句话:

  • interface是类型
  • interface类型的变量可以赋值
  • 任何实现了interface类型的具体类型变量,都可以赋值给interface类型的变量
时间: 2024-10-22 08:12:07

【Go语言】【14】GO语言的接口类型的相关文章

编译型与解释型、动态语言与静态语言、强类型语言与弱类型语言的区别

一.编译型和解释型 我们先看看编译型,其实它和汇编语言是一样的:也是有一个负责翻译的程序来对我们的源代码进行转换,生成相对应的可执行代码.这个过程说得专业一点,就称为编译(Compile),而负责编译的程序自然就称为编译器(Compiler).如果我们写的程序代码都包含在一个源文件中,那么通常编译之后就会直接生成一个可执行文件,我们就可以直接运行了.但对于一个比较复杂的项目,为了方便管理,我们通常把代码分散在各个源文件中,作为不同的模块来组织.这时编译各个文件时就会生成目标文件(Object  

弱类型语言和强类型语言

最近开始频繁接触H5,同事说脚本语言大多都是弱类型语言.看了下下弱类型语言和强类型语言的东西. 动态语言和静态语言.1 动态类型语言:动态类型语言是指在运行期间才去做数据类型检查的语言,也就是说,在用动态类型的语言编程时,永远也不用给任何变量指定数据类型,该语言会在你第一次赋值给变量时,在内部将数据类型记录下来.Python和Ruby就是一种典型的动态类型语言,其他的各种脚本语言如VBScript也多少属于动态类型语言.2 静态类型语言:静态类型语言与动态类型语言刚好相反,它的数据类型是在编译其

[转]Lua语言基础汇总(1) -- 类型与值

基础介绍 Lua是一种动态类型的语言.在语言中没有类型定义的语法,每个值都带有其自身的类型信息.在Lua中有8种基本类型,分别是: nil(空)类型 boolean(布尔)类型 number(数字)类型 string(字符串)类型 userdata(自定义类型) function(函数)类型 thread(线程)类型 table(表)类型 以上是Lua中的8中基本类型,我们可以使用type函数,判断一个值得类型,type函数返回一个对应类型的字符串描述.例如: 1 2 3 4 5 6 7 8 9

静态语言与动态语言,强类型语言与弱类型语言

Dynamic Programming Language (动态语言或动态编程语言) Dynamically Typed Language (动态类型语言) Statically Typed Language (静态类型语言) 动态语言,准确地说,是指程序在运行时可以改变其结构:新的函数可以被引进,已有的函数可以被删除等在结构上的变化.比如众所周知的ECMAScript(JavaScript)便是一个动态语言.除此之外如Ruby.Python等也都属于动态语言,而C.C++等语言则不属于动态语言

Java语言中反射动态代理接口的解释与演示

Java语言中反射动态代理接口的解释与演示 Java在JDK1.3的时候引入了动态代理机制.可以运用在框架编程与平台编程时候捕获事件.审核数据.日志等功能实现,首先看一下设计模式的UML图解: 当你调用一个接口API时候,实际实现类继承该接口,调用时候经过proxy实现. 在Java中动态代理实现的两个关键接口类与class类分别如下: java.lang.reflect.Proxy java.lang.reflect.InvocationHandler 我们下面就通过InvocationHan

动态语言和静态语言、编译型语言和解释型语言、强类型语言和弱类型语言的分析

一.动态语言和静态语言1. 我们常说的动.静态语言,通常是指: 动态类型语言 Dynamically Typed Language 静态类型语言 Statically Typed Language 可能还有:动.静态编程语言 Dynamic\Statically Programming Language 2.    动态类型语言:在运行期间检查数据的类型的语言例如:Ruby\Python这类语言编程,不会给变量指定类型,而是在附值时得到数据类型.Python是动态语言,变量只是对象的引用,变量a

初探swift语言的学习笔记(可选类型?和隐式可选类型!)

可选类型.隐式可选类型 其次swift还引入一个较有趣的初始值设置语法使用"?"操作符及"!"号操作符 如:"var optionalString: String? = "Hello" optionalString == nil var optionalName: String? = "John Appleseed" var greeting = "Hello!" if let name = op

强/若类型语言 动/静态语言

弱/强类型指的是语言类型系统的类型检查的严格程度.动/静态指的是变量与类型的绑定方法. 弱类型相对于强类型来说类型检查更不严格,比如说允许变量类型的隐式转换,允许强制类型转换等等.强类型语言一般不允许这么做. 弱类型语言 变量在进行运算时,会做隐式的转换类型强类型语言 变量的值的数据类型一旦确定,使用时不能改变 动态语言:编译时不知道数据类型,只有在执行时才知道数据类型静态语言:编译的时候进行数据类型检查,知道每一个变量的类型 静态类型指的是编译器在compile time执行类型检查,动态类型

Go语言学习笔记(二) [变量、类型、关键字]

日期:2014年7月19日 1.Go 在语法上有着类 C 的感觉.如果你希望将两个(或更多)语句放在一行书写,它们 必须用分号分隔.一般情况下,你不需要分号. 2.Go 同其他语言不同的地方在于变量的类型在变量名的后面.例如:不是,int a,而是 a int.当定义了一个变量,它默认赋值为其类型的 null 值.这意味着,在 var a int后,a 的 值为 0.而 var s string,意味着 s 被赋值为零长度字符串,也就是 "". 3.Go语言的变量声明和赋值 在Go中使