简单地说 Interface是一组Method的组合,可以通过Interface来定义对象的一组行为。
如果某个对象实现了某个接口的所有方法,就表示它实现了该借口,无需显式地在该类型上添加接口说明。

Interface是一个方法的集合,它里面没有其他类型变量,而且Method只用定义原型 不用实现

①接口定义

1.命名时习惯以"er"结尾,如Printer Reader Writer

2.一个Interface的Method不宜过多,一般0~3个

3.一个Interface可以被任意的对象事项;相应地,一个对象也可以实现多个Interface

示例:

type People struct{
Name string
}
type Student struct{
People
School string
}
type Teacher struct{
People
Department string
}
func (p People) SayHi(){}
func (s Student) SayHi(){}
func (t Teacher) SayHi(){}
func (s Student) Study(){} //根据struct的方法提取接口 从而使struct自动实现了该接口
type Speaker interface{
SayHi()
}
type Learner interface{
SayHi()
Study()
}

上面的例子中,Speaker接口被对象People,Teacher,Student实现;而Student同时实现了接口Speaker和Learner。

接口组合:

type SpeakLearner interface {
Speaker
Learner
}//组合后使得SpeakLearner具有Speaker和Learner的功能

空接口:

任何类型都实现了空接口,相当于Java中的Object类

func test(a interface{}){}//该方法可以接受任意类型(int rune float32 struct...)的参数

②接口执行机制和接口赋值

首先介绍一种Go语言带接收者(Receiver)的函数机制(下面的两种情况执行结果一样,涉及到struct成员值改变时仍然一样)

情况1:

package main

import (
"fmt"
) type People struct {
Name string
} func (p People) SayHi(){ //此处的Receiver是strcut
fmt.Println("hello, this is", p.Name)
}
func (p *People) Study(){//此处的Receiver是****struct
fmt.Printf("%s is studying\n", p.Name)
}
type SpeakLearner interface {
SayHi()
Study()
}
func main() {
people := People{"zhangsan"}//这里的people为People类型
people.SayHi()
people.Study()
}

情况2:

func main() {
people := &People{"zhangsan"}//这里的people为**People类型,即指针
people.SayHi()
people.Study()
}

通过上面的例子可以看出Receiver为People和*People的函数均可被People或者*People两种类型调用,接下来借可能有在调用过程中People与*People之间的转换问题

看下面的例子:

package main

import (
"fmt"
)
type Example struct{
Integer1 int
Integer2 int
}
func (e Example) Assign(num1 int, num2 int) {
e.Integer1, e.Integer2 = num1, num2
}
func (e *Example) Add(num1 int, num2 int) {
e.Integer1 +=num1
e.Integer2 +=num2
}
func main(){
var e1 Example = Example{,}
e1.Assign(,)
fmt.Println(e1) e1.Add(,)
fmt.Println(e1) var e2 *Example = &Example{,}
e2.Assign(,)
fmt.Println(e2) e2.Add(,)
fmt.Println(e2)
}

以上程序的执行结果为:

{,}
{,}
&{,}
&{,}

可以看出实际执行的过程按函数定义前的Receiver类型执行。

对于接口的执行机制:

1.T仅拥有属于T类型的方法集,而*T则同时拥有(T+*T)方法集
2.基于T实现方法,表示同时实现了interface和interface(*T)接口
3.基于*T实现方法,那就只能是对interface(*T)实现接口

type Integer int

func (a Integer) Less(b Integer) bool {
return a < b
}
func (a *Integer) Add(b Integer) {
*a += b
}
相应地,我们定义接口LessAdder,如下:
type LessAdder interface {
Less(b Integer) bool
Add(b Integer)
} 现在有个问题:假设我们定义一个Integer类型的对象实例,怎么将其赋值给LessAdder接口呢?
应该用下面的语句(),还是语句()呢?
var a Integer =
var b LessAdder = &a ... ()
var b LessAdder = a ... ()
答案是应该用语句()。原因在于,Go语言可以根据下面的函数:
func (a Integer) Less(b Integer) bool
即自动生成一个新的Less()方法:
func (a *Integer) Less(b Integer) bool {
return (*a).Less(b)
}
这样,类型*Integer就既存在Less()方法,也存在Add()方法,满足LessAdder接口。 而从另一方面来说,根据
func (a *Integer) Add(b Integer)
这个函数无法自动生成以下这个成员方法:
func (a Integer) Add(b Integer) {
(&a).Add(b)
}
因为(&a).Add()改变的只是函数参数a,对外部实际要操作的对象并无影响,这不符合用
户的预期。所以,Go语言不会自动为其生成该函数。
因此,类型Integer只存在Less()方法,缺少Add()方法,不满足LessAdder接口,故此上面的语句()不能赋值。

接口赋值举例:

package main
import(
"fmt"
)
//定义对象People、Teacher和Student
type People struct {
Name string
}
type Teacher struct{
People
Department string
}
type Student struct{
People
School string
}
//对象方法实现
func (p People) SayHi() {
fmt.Printf("Hi, I'm %s. Nice to meet you!\n",p.Name)
}
func (t Teacher) SayHi(){
fmt.Printf("Hi, my name is %s. I'm working in %s .\n", t.Name, t.Department)
}
func (s Student) SayHi() {
fmt.Printf("Hi, my name is %s. I'm studying in %s.\n", s.Name, s.School)
}
func (s Student) Study() {
fmt.Printf("I'm learning Golang in %s.\n", s.School)
}
//定义接口Speaker和Learner
type Speaker interface{
SayHi()
}
type Learner interface{
SayHi()
Study()
}
func main() {
people := People{"张三"}
teacher := Teacher{People{"郑智"}, "Computer Science"}
student := Student{People{"李明"}, "Yale University"} var is Speaker //定义Speaker接口类型的变量
is = people //is能存储People
is.SayHi() is = teacher //is能存储Teacher
is.SayHi() is = student
is.SayHi() //is能存储Student var il Learner
il = student //Learner类型接口的变量能存储Student
il.Study()
}

执行结果为:

Hi, I'm 张三. Nice to meet you!
Hi, my name is 郑智. I'm working in Computer Science .
Hi, my name is 李明. I'm studying in Yale University.
I'm learning Golang in Yale University.

通过这个例子可以 看到(如同Java等语言)接口机制在多态和创建可扩展可重用的代码时的重要作用

③匿名字段和接口转换

若果接口类型S内部嵌入了接口类型T(匿名),则接口匿名字段方法集规则如下:

1.如果S嵌入匿名类型T,则S方法集包含T方法集。
2.如果S嵌入匿名类型*T,则S方法集包含*T方法集(包括Riceiver为T和*T的方法)。
3.如果S嵌入匿名类型T或*T,则*S方法集包含*T方法集(包括Riceiver为T和*T的方法)。(重要)

例如:

package main
import(
"fmt"
) type People struct {
Name string
}
type S1 struct{
People //S1类型嵌入匿名People
Department string
}
type S2 struct{
*People //S2类型嵌入匿名*People
Department string
}
func (p People) Say1() {
fmt.Printf("Hi, I'm %s. Say1111\n",p.Name)
}
func (p *People) Say2() {
fmt.Printf("Hi, I'm %s. Say2222\n",p.Name)
}
type Speaker interface{
Say1()
Say2()
} func main() {
people := People{"张三"}
s1 := S1{People{"郑智"}, "Computer Science"}
s2 := S2{&People{"李明"}, "Math"} var is Speaker
is = &people //*People实现了Speaker接口
is.Say1()
is.Say2() //is = s1 //S1类型嵌入匿名People 不存在Say2()方法 因而未实现Speaker接口
//错误提示: cannot use s1 (type S1) as type Speaker in assignment:
//S1 does not implement Speaker (Say2 method has pointer receiver) is = s2 //S2类型嵌入匿名*People 因而(p People) Say1()和(p *People) Say2()方法都有 实现了Speaker接口
is.Say1()
is.Say2() is = &s1 //S1类型嵌入匿名People *S1 实现了Speaker接口
is.Say1()
is.Say2() is = &s2 //S2类型嵌入匿名*People *S2 实现了Speaker接口
is.Say1()
is.Say2() }

执行结果为:

Hi, I'm 张三. Say1111
Hi, I'm 张三. Say2222
Hi, I'm 李明. Say1111
Hi, I'm 李明. Say2222
Hi, I'm 郑智. Say1111
Hi, I'm 郑智. Say2222
Hi, I'm 李明. Say1111
Hi, I'm 李明. Say2222

从而证明了匿名字段方法集的3条规则。

接口转换类似于说是接口继承规则 可认为是实现复杂接口(方法多)向简单接口(方法少)转换,其中简单接口中的方法在复杂接口中均有声明 。例如:

package main
import(
"fmt"
)
type People struct {
Name string
}
type Student struct{
People
School string
}
func (p People) GetPeopleInfo() {
fmt.Println(p)
}
func (s Student) GetStudentInfo() {
fmt.Println(s)
}
type PeopleInfo interface{
GetPeopleInfo()
}
type StudentInfo interface{
GetPeopleInfo()
GetStudentInfo()
}
func main() {
var is StudentInfo = Student{People{"李明"}, "Yele University"}
is.GetStudentInfo()
is.GetPeopleInfo() var ip PeopleInfo = is
ip.GetPeopleInfo()
///ip.GetStudentInfo() note:ip.GetStudentInfo undefined
}

④接口类型推断:Comma-ok断言和Switch测试

利用接口类型推断可以 反向知道接口类型变量里面实际保存的是哪一种类型的对象。

Go语言中,常用两种方法可以进行接口类型推断,即Comma-ok断言和Switch测试

Comma-ok断言使用格式如下

value,ok = element.(T)

用法示例:

//利用Comma-ok断言进行接口类型推断
package main
import(
"fmt"
) type People struct{
Name string
Age int
} //定义空接口用于存储任意类型数据类型
type Object interface{} func main() {
people := People{"张三", }
objs := make([]Object, )
objs[], objs[], objs[], objs[] = , true, "Hello", people
for index, element := range objs{
if value, ok := element.(int); ok{
fmt.Printf("objs[%d]类型是int,value=%d\n", index, value)
}else if value, ok := element.(bool); ok{
fmt.Printf("objs[%d]类型是bool,value=%v\n", index, value)
}else if value, ok := element.(string); ok{
fmt.Printf("objs[%d]类型是string,value=%s\n", index, value)
}else if value, ok := element.(People); ok{
fmt.Printf("objs[%d]类型是Peole,value=%v\n", index, value)
}else{
fmt.Printf("objs[%d]类型未知\n", index)
}
}
}

结果是这样的:

objs[]类型是int,value=
objs[]类型是bool,value=true
objs[]类型是string,value=Hello
objs[]类型是Peole,value={张三 }

使用Switch测试判断接口类型,程序结构更加简洁,示例如下(只修改了示例中的main函数):

func main() {
people := People{"张三", }
objs := make([]Object, )
objs[], objs[], objs[], objs[] = , true, "Hello", people
for index, element := range objs{ switch value := element.(type){
case int:
fmt.Printf("objs[%d]类型是int,value=%d\n", index, value)
case bool:
fmt.Printf("objs[%d]类型是bool,value=%v\n", index, value)
case string:
fmt.Printf("objs[%d]类型是string,value=%s\n", index, value)
case People:
fmt.Printf("objs[%d]类型是Peole,value=%v\n", index, value)
default:
fmt.Printf("objs[%d]类型未知\n", index)
}
}
}

执行结果Comma-ok方法相同,但是程序简洁了许多。

【Go语言】面向对象扩展——接口的更多相关文章

  1. Go语言基础之接口(面向对象编程下)

    1 接口 1.1 接口介绍 接口(interface)是Go语言中核心部分,Go语言提供面向接口编程,那么接口是什么? 现实生活中,有许多接口的例子,比如说电子设备上的充电接口,这个充电接口能干什么, ...

  2. hibernate核心接口,和扩展接口。回顾笔记,以前没记,现在补上,纯手工敲的。

    hibernate核心接口: 所有的hibernate应用都会访问hibernate的5个核心接口 1,Configuration接口 Configuration用于配置并且根启动Hibernate. ...

  3. Hadoop Hive概念学习系列之hive里的扩展接口(CLI、Beeline、JDBC)(十六)

    <Spark最佳实战  陈欢>写的这本书,关于此知识点,非常好,在94页. hive里的扩展接口,主要包括CLI(控制命令行接口).Beeline和JDBC等方式访问Hive. CLI和B ...

  4. Spring8:一些常用的Spring Bean扩展接口

    前言 Spring是一款非常强大的框架,可以说是几乎所有的企业级Java项目使用了Spring,而Bean又是Spring框架的核心. Spring框架运用了非常多的设计模式,从整体上看,它的设计严格 ...

  5. Java面向对象:接口

    Java面向对象之接口 什么是接口:接口是一种规范和标准,他们可以约束类的行为,是一些方法特征的集合 语法: [修饰符] interface 接口名 extends 父接口1,夫接口2....... ...

  6. php调用一个c语言写的接口问题

    用php调用一个c语言写的soap接口时,遇到一个问题:不管提交的数据正确与否,都无法请求到接口 1.用php标准的soap接口去请求 2.拼接xml数据去请求 以上两种方式都不正确 解决办法:php ...

  7. PHP安装memcache扩展接口步骤

    1.将php_memcache.dll文件保存到php的应用程序扩展ext目录中 2.在php.ini配置文件添加扩展的位置,加入一行extension=php_memcache.dll 3.重新启动 ...

  8. C语言与MATLAB接口 编程与实例 李传军编着

    罗列一下以前自己学习C语言与MATLAB混编的笔记,顺便复习一遍. <C语言与MATLAB接口 编程与实例 李传军编着>(未看完,目前看到P106) 目录P4-8 ************ ...

  9. C语言面向对象风格编程

    前言 本文略谈C面向对象风格编程,如何使用过程式语言去模拟面向对象的特性?C面向对象的方式和形式很多,不一而足,本文的代码形式是模拟部分C++面向对象关键词并赋予其特性,这种方式对于初级程序员比较好理 ...

随机推荐

  1. android开子线程避免出现main错误

    Runnable SonThread=new Runnable() { @Override public void run() { // TODO Auto-generated method stub ...

  2. ListView遍历每个Item出现NullPointerException的异常(转)

    在使用ListView过程中我们有时候需要遍历取得每个Item项中的一些数据(比如每个Item里面有TextView,需要获取它的文本等等),但是我们在遍历过程中经常会遇到NullPointerExc ...

  3. 记录linux /bin被误删除的解决过程

    1.事因: 执行shell测试时,shell中rm -rf $path/* 变量$path为空,结果执行的命令是rm -rf / 事发时及时ctrl+c中断,导致只有/bin /boot目录删除 2. ...

  4. PhpMyAdmin管理,登录多台远程MySQL服务器

    法一: 可直接在config.inc.php里添加数据库连接信息即可 先$i++, 然后复制原来的配置信息后修改 不过这种方式需要将连接信息写在配置文件中,有点麻烦. 这种后面省事,不用填信息,选择一 ...

  5. Android打包的那些事

    使用gradle打包apk已经成为当前主流趋势,我也在这个过程中经历了各种需求,并不断结合gradle新的支持,一一改进.在此,把这些相关的东西记录,做一总结. 1. 替换AndroidManifes ...

  6. yii2.0 的数据的 查 删

    数据的查询 /**     * 查询正在使用的数据 model 层     */ public function selectdata(){ return $this->find()->a ...

  7. mybatis做like模糊查询

    http://www.cnblogs.com/cyttina/p/3894428.html

  8. 代码审查工具StyleCop

    “代码审查”或是“代码评审”(Code Review),这是一个流程,当开发人员写好代码后,需要让别人来review一下他的代码,这是一种有效发现BUG的方法.由此,我们可以审查代码的风格.逻辑.思路 ...

  9. Storm与Spark:谁才是我们的实时处理利器

    Storm与Spark:谁才是我们的实时处理利器 ——实时商务智能目前已经逐步迈入主流,而Storm与Spark开源项目的支持无疑在其中起到了显著的推动作用.那么问题来了:实时处理到底哪家强? 实时商 ...

  10. 【转】crontab定时任务中文乱码问题

    转载:http://blog.163.com/rettar@126/blog/static/1216503422012135511740/ 手动执行都很正常的的脚步,添加到定时任务中一直执行失败,日志 ...