接口——定义,实现接口的条件,类型与接口的关系,类型断言

1、定义

Go 语言的接口设计是非侵入式的,接口编写者无须知道接口被哪些类型实现。而接口实现者只需知道实现的是什么样子的接口,但无须指明实现哪一个接口。编译器知道最终编译时使用哪个类型实现哪个接口,或者接口应该由谁来实现。

每个接口类型由数个方法组成。接口的形式代码如下:

type 接口类型名 interface{
方法名1( 参数列表1 ) 返回值列表1
方法名2( 参数列表2 ) 返回值列表2

}

说明:

  • 接口类型名:使用 type 将接口定义为自定义的类型名。Go语言的接口在命名时,一般会在单词后面添加 er,如有写操作的接口叫 Writer,有字符串功能的接口叫 Stringer,有关闭功能的接口叫 Closer 等。
  • 方法名:当方法名首字母是大写时,且这个接口类型名首字母也是大写时,这个方法可以被接口所在的包(package)之外的代码访问。
  • 参数列表、返回值列表:参数列表和返回值列表中的参数变量名可以被忽略

2、实现接口的条件

如果一个任意类型 T 的方法集为一个接口类型的方法集的超集,则我们说类型 T 实现了此接口类型。T 可以是一个非接口类型,也可以是一个接口类型。
实现关系在 Go语言中是隐式的。两个类型之间的实现关系不需要在代码中显式地表示出来。Go语言中没有类似于 implements 的关键字。 Go编译器将自动在需要的时候检查两个类型之间的实现关系。
接口定义后,需要实现接口,调用方才能正确编译通过并使用接口。接口的实现需要遵循两条规则才能让接口可用。

1)接口的方法与实现接口的类型方法格式一致

在类型中添加与接口签名一致的方法就可以实现该方法。签名包括方法中的名称、参数列表、返回参数列表。也就是说,只要实现接口类型中的方法的名称、参数列表、返回参数列表中的任意一项与接口要实现的方法不一致,那么接口的这个方法就不会被实现。

2)接口中所有方法均被实现

当一个接口中有多个方法时,只有这些方法都被实现了,接口才能被正确编译并使用。

示例:

package main
import (
	"fmt"
)
// 定义一个数据写入器
type DataWriter interface {
	WriteData(data interface{}) error
}
// 定义文件结构,用于实现DataWriter
type file struct {
}
// 实现DataWriter接口的WriteData方法
func (d *file) WriteData(data interface{}) error {
	fmt.Println("WriteData:", data)
	return nil
}
func main() {
	// 实例化file
	f := new(file)
	// 声明一个DataWriter的接口
	var writer DataWriter
	// 将接口赋值f,也就是*file类型
	writer = f
	// 使用DataWriter接口进行数据写入
	writer.WriteData("data")
}

  

3、类型与接口的关系

在 Go语言中类型和接口之间有一对多和多对一的关系。

1)一个类型可以同时实现多个接口,而接口间彼此独立,不知道对方的实现。

package main

import (
	"fmt"
	"io"
)

type Writer interface {
	Write(p []byte) (n int, err error)
}
type Closer interface {
	Close() error
}
type Socket struct {
}
func (s *Socket) Write(p []byte) (n int, err error) {
	fmt.Println("Socket write")
	return 0, nil
}
func (s *Socket) Close() error {
	fmt.Println("Socket close")
	return nil
}
// 使用io.Writer的代码, 并不知道Socket和io.Closer的存在
func usingWriter( writer io.Writer){
	writer.Write( nil )
}
// 使用io.Closer, 并不知道Socket和io.Writer的存在
func usingCloser( closer io.Closer) {
	closer.Close()
}
func main() {
	// 实例化Socket
	s := new(Socket)
	usingWriter(s)
	usingCloser(s)
}

  

2)多个类型可以实现相同的接口

一个接口的方法,不一定需要由一个类型完全实现,接口的方法可以通过在类型中嵌入其他类型或者结构体来实现。也就是说,使用者并不关心某个接口的方法是通过一个类型完全实现的,还是通过多个结构嵌入到一个结构体中拼凑起来共同实现的。

示例:

package main

import "fmt"

// 一个服务需要满足能够开启和写日志的功能
type Service interface {
	Start()  // 开启服务
	Log(string)  // 日志输出
}
// 日志器
type Logger struct {
}
// 实现Service的Log()方法
func (g *Logger) Log(l string) {
	fmt.Println(l)
}
// 游戏服务
type GameService struct {
	Logger  // 嵌入日志器
}
// 实现Service的Start()方法
func (g *GameService) Start() {
	fmt.Println("游戏服启动")
}
func main() {
	var s Service = new(GameService)
	s.Start()
	s.Log("hello")
}

  

4、类型断言

Go语言中有四种接口相关的类型转换情形:

  • 将一个非接口值转换为一个接口类型。在这样的转换中,此非接口值的类型必须实现了此接口类型。
  • 将一个接口值转换为另一个接口类型(前者接口值的类型实现了后者目标接口类型)。
  • 将一个接口值转换为一个非接口类型(此非接口类型必须实现了此接口值的接口类型)。
  • 将一个接口值转换为另一个接口类型(前者接口值的类型可以实现了也可以未实现后者目标接口类型)。

一个类型断言表达式的语法为 i.(T),其中 i 为一个接口值, T 为一个类型名或者类型字面表示。 类型 T 可以为任意一个非接口类型,或者一个任意接口类型。
在一个类型断言表达式 i.(T) 中, i 称为断言值, T 称为断言类型。 一个断言可能成功或者失败。

对于 T 是一个非接口类型的情况,如果断言值 i 的动态类型存在并且此动态类型和 T 为同一类型,则此断言将成功;否则,此断言失败。 当此断言成功时,此类型断言表达式的估值结果为断言值 i 的动态值的一个复制。可以把此种情况看作是一次拆封动态值的尝试。

对于 T 是一个接口类型的情况,当断言值 i 的动态类型存在并且此动态类型实现了接口类型 T,则此断言将成功;否则,此断言失败。 当此断言成功时,此类型断言表达式的估值结果为一个包裹了断言值i的动态值的一个复制的 T 值。

一个失败的类型断言的估值结果为断言类型的零值。

按照上述规则,如果一个类型断言中的断言值是一个零值 nil 接口值,则此断言必定失败。

对于大多数场合,一个类型断言被用做一个单值表达式。 但是,当一个类型断言被用做一个赋值语句中的唯一源值时,此断言可以返回一个可选的第二个结果并被视作为一个多值表达式。此可选的第二个结果为一个类型不确定的布尔值,用来表示此断言是否成功了。

注意:如果一个断言失败并且它的可选的第二个结果未呈现,则此断言将造成一个恐慌。

示例1:断言类型为非接口类型

package main
import "fmt"
func main() {
	// 编译器将把123的类型推断为内置类型int。
	var x interface{} = 123
	// 情形一:
	n, ok := x.(int)
	fmt.Println(n, ok) // 123 true
	n = x.(int)
	fmt.Println(n) // 123
	// 情形二:
	a, ok := x.(float64)
	fmt.Println(a, ok) // 0 false
	// 情形三:
	a = x.(float64) // 将产生一个恐慌,抛出异常
}

  

示例2:

package main
import "fmt"
type Writer interface {
	Write(buf []byte) (int, error)
}
type DummyWriter struct{}
func (DummyWriter) Write(buf []byte) (int, error) {
	return len(buf), nil
}
func main() {
	var x interface{} = DummyWriter{}
	// y的动态类型为内置类型string。
	var y interface{} = "abc"
	var w Writer
	var ok bool
	// DummyWriter既实现了Writer,也实现了interface{}。
	w, ok = x.(Writer)
	fmt.Println(w, ok) // {} true
	x, ok = w.(interface{})
	fmt.Println(x, ok) // {} true
	// y的动态类型为string。string类型并没有实现Writer。
	w, ok = y.(Writer)
	fmt.Println(w, ok) // <nil> false
	w = y.(Writer) // 将产生一个恐慌
}

  

原文地址:https://www.cnblogs.com/ACGame/p/11923505.html

时间: 2024-10-08 06:10:40

接口——定义,实现接口的条件,类型与接口的关系,类型断言的相关文章

第37条:用标记接口定义类型

标记接口是没有包含方法声明的接口,而只是指明一个类实现了具有某种属性的接口.考虑Serializable接口,通过实现这个接口,类表明它的实例可以被写到ObjectOutputStream. 标记接口相比标记注解的优点: 1.标记接口定义的类型是由被标记类的实例实现的:标记注解则没有定义这样的类型. 2. 可以被更精确地进行锁定.如果注解类型利用@Target(ElementType.TYPE)声明,它就可以被应用到任何类或者接口,假设有一个标记只是适用于特殊的接口实现,但它却可以被应用到类,如

1.接口定义与http协议

接口定义 接口是个比较泛义上的概念,主要表示系统对外交互的部分,比如电源插座是电器和电能之间的接口,图形界面是应用软件和用户的接口,医院挂号大厅是医生和病人之间的接口 webAPI 我们要学习的接口概念缩小到web系统提供的对外消息交互接口,通过发送对应的请求给服务器,服务器会返回相应的结果,因为其调用模式非常像编程语言中的API,所以web消息交互接口又叫webapi. Web服务接口 目前web服务接口都是基于http协议传递消息 通常大家所说的,web服务接口,websevice ,web

JAVA8新特性——接口定义增强

JAVA9都要出来了,JAVA8新特性都没搞清楚,是不是有点掉队哦~ 接口定义增强 在JDK1.8以前,接口是定义的: 接口(英文:Interface),在JAVA编程语言中是一个抽象类型,是抽象方法的集合,接口通常以interface来声明.一个类通过继承接口的方式,从而来继承接口的抽象方法. 在JDK1.8之前,接口有如下特性: 接口中每一个方法也是隐式抽象的,接口中的方法会被隐式的指定为 public abstract(只能是 public abstract,其他修饰符都会报错). 接口中

我的编码习惯 - 接口定义

原文出处: 晓风轻 工作中,少不了要定义各种接口,系统集成要定义接口,前后台掉调用也要定义接口.接口定义一定程度上能反应程序员的编程功底.列举一下工作中我发现大家容易出现的问题: 1. 返回格式不统一 同一个接口,有时候返回数组,有时候返回单个:成功的时候返回对象,失败的时候返回错误信息字符串.工作中有个系统集成就是这样定义的接口,真是辣眼睛.这个对应代码上,返回的类型是map,json,object,都是不应该的.实际工作中,我们会定义一个统一的格式,就是ResultBean,分页的有另外一个

CLR设计类型之接口(一)

写到这一节的时候,CLR设计类型就已经结束了,因为CLR要求的是有一定基础的人看的,所以我们不是从基础类型以及运算符开始的,文章从一开始就讲的是深入面向对象编程,研究C#程序的设计模式.C#面向对象编程有三个特点:封装,继承,多态.接口的实现就是实现继承 其实在开始之前说一下这两天发生的事情,前几天维护项目代码时,虽然是自己写得但是由于逻辑判断比较多,有些变量名起的也不是很有意义,在看的时候就完全忘记当初为啥要写成这样了,也是有点汗颜,所以最近就把代码整洁之道也放在了看书的目录上,今后的示例代码

C#接口定义

C#不支持多重继承,但是客观世界出现多重继承的情况又比较多.为了避免传统的多重继承给程序带来的复杂性等问题,C# 提出了接口的概念.通过接口可以实现多重继承的功能.  继承该接口的类或结构要与接口的定义严格一致.接口描述可属于任何类或结构的一组相关行为.接口可由方法.属性.事件.索引器或这4种成员类型的任何组合构成.接口不能包含字段.接口成员一定是公共的.  类和结构可以像类继承基类或结构一样从接口继承,而且可以继承多个接口.当类或结构继承接口时,它继承成员定义但不继承实现.若要实现接口成员,类

Java接口定义(interface)及使用(implements)

1. 在类的声明中,通过关键字extends来创建一个类的子类.一个类通过关键字implements声明自己使用一个或者多个接口.extends 是继承某个类, 继承之后可以使用父类的方法, 也可以重写父类的方法; implements 是实现多个接口, 接口的方法一般为空的, 必须重写才能使用2.extends是继承父类,只要那个类不是声明为final或者那个类定义为abstract的就能继承,JAVA中不支持多重继承,但是可以用接口 来实现,这样就要用到implements,继承只能继承一个

接口定义规范

良好的编码规范应该是程序员的一种职业素养,不仅仅是对自己的要求,也是对工作以及职业的尊重.代码不仅仅是给自己看的,也要供其他同事调用.调试,而且也要运行在jvm虚拟机上,健壮性.高效率.可读性强.标准化应该是大家都追求的目标. 不规范的代码和开发习惯使工作中的大部分时间都在定位问题 + 改代码,填堵遗留下来的坑,导致实际用于开发中的时间并不多,高质量.高效的代码,可以切实有效的提高工作效率,减少无谓的时间浪费,也让大家在工作中少踩坑,并且杜绝踩重复的坑.本文有参照知乎晓风轻和孤尽两位大神的文章,

java接口定义和作用

接口语法 1.接口是一种引用类型,可以等同看作类.修饰符 interface 接口名 2.接口中只能出现常量和抽象方法 3.接口其实是一个特殊的抽象类,特殊在接口是完全抽象的 4.接口中没有构造方法,接口也无法实例化 5.接口和接口之间可以多继承 6.一个类可以实现多个接口,这里的实现等同于继承 7.一个非抽象的类实现接口,需要将接口中所有方法实现/重写/覆盖 #java package study1; public interface A{ //常量都是public static final,