当前位置: 首页 > news >正文

Go语言程序设计-第7章--接口

Go语言程序设计-第7章–接口

接口类型是对其他类型行为的概括与抽象。

Go 语言的接口的独特之处在于它是隐式实现。对于一个具体的类型,无须声明它实现了哪些接口,只要提供接口所必须实现的方法即可。

7.1 接口即约定

7.2 接口类型

package iotype Reader interface {Read(p []byte) (n int, err error)
}type Closer interface {Close() error
}

组合已有接口得到新的接口。

  • 嵌入式声明
type ReadWriter interface {ReaderWriter
}
  • 直接声明
type ReadWriter interface {Read(p []byte) (n int, err error)Close() error
}
  • 混合声明
type ReadWriter interface {Read(p []byte) (n int, err error)Writer
}

7.3 实现接口

接口的实现规则很简单,仅当一个表达式实现了一个接口时,这个表达式才可以赋值给接口。

var w io.Writer
w = os.Stdout // OK: *os.File 有 Writable 方法
w = new(bytes.Buffer) // OK: * bytes.Buffer 有 Writable 方法
w = time.Second // 编译错误: time.Duration 没有 Writable 方法=

如果方法的接收者是一个指针类型,就不可以从一个无地址的对象上调用该方法,如:

type IntSet struct {}
func (*IntSet) String() stringvar _ = IntSet{}.String() // 编译错误:String 方法需要 *IntSet 接收者

可以从一个 IntSet 变量上调用此方法

var s IntSet
var _ = s.String() // OK: s 是一个变量,&s 有 String 方法。

因为只有 *IntSet 有 String 方法,所以也只有 *IntSet 实现了 fmt.Stringer 接口。

var _ fmt.Stringer = &s // OK
var _ fmt.Stringer = s // 编译错误: IntSet 缺少 String 方法。

可以把任何数据赋值给空接口类型。

var any interface{} // 空接口类型
any = true
any = 12.34
any = "hello"
any = map[string]int {"one":1}
fmt.Println(any)

判断是否实现接口只需要比较具体类型和接口类型的方法,所以没有必要在具体类型的定义中声明这种关系。

非空的接口类型(如 io.Writer)通常由一个指针类型来实现,特别是当接口类型的一个或多个方法暗示会修改接收者的情形。一个指向结构的指针才是最常见的方法接收者。

7.4 使用 flat.Value 来解析参数

tempflag.go

package mainimport ("flag""fmt"
)type Celsius float64
type Fahrenheit float64func CToF(c Celsius) Fahrenheit { return Fahrenheit(c*9.0/5.0 + 32.0) }
func FToC(f Fahrenheit) Celsius { return Celsius((f - 32.0) * 5.0 / 9.0) }func (c Celsius) String() string { return fmt.Sprintf("%g°C", c) }/*
//!+flagvalue
package flag// Value is the interface to the value stored in a flag.
type Value interface {String() stringSet(string) error
}
//!-flagvalue
*///!+celsiusFlag
// *celsiusFlag satisfies the flag.Value interface.
type celsiusFlag struct{ Celsius }func (f *celsiusFlag) Set(s string) error {var unit stringvar value float64fmt.Sscanf(s, "%f%s", &value, &unit) // no error check neededswitch unit {case "C", "°C":f.Celsius = Celsius(value)return nilcase "F", "°F":f.Celsius = FToC(Fahrenheit(value))return nil}return fmt.Errorf("invalid temperature %q", s)
}//!-celsiusFlag//!+CelsiusFlag// CelsiusFlag defines a Celsius flag with the specified name,
// default value, and usage, and returns the address of the flag variable.
// The flag argument must have a quantity and a unit, e.g., "100C".
func CelsiusFlag(name string, value Celsius, usage string) *Celsius {f := celsiusFlag{value}flag.CommandLine.Var(&f, name, usage)return &f.Celsius
}
var temp = CelsiusFlag("temp", 20.0, "the temperature")
func main() {flag.Parse()fmt.Println(*temp)
}

7.5 接口值

一个接口类型的值(简称接口值)其实有两个部分:一个具体类型和该类型的一个值。二者称为接口的动态类型和动态值。

类型描述符来提供每个类型的具体信息,比如它的名字和方法。对于一个接口值,类型部分就用对应的类型描述符来表述。

如下四个语句中,变量 w 有三个不同的值(最初和最后是同一个值,都是 nil)

var w io.Writer
w = os.Stdout
w = new(bytes.Buffer)
w = nil

每个语句后 w 的值和相关的动态行为。第一个语句声明了 w: var w io.Writer
在 Go 语言中,变量总是初始化为一个特定的值,接口也不例外。接口的零值就是把它的动态类型和值都设置为 nil

一个接口值是否是 nil 取决于它的动态类型,所以现在这是一个 nil 接口值。可以用 w == nil 或者 w != nil 来检测一个接口值是否是 nil

第二个语句把一个 *os.File 类型的值赋给了 w: w = os.Stdout
这次赋值把一个具体类型隐私转换为一个接口类型,它与对应的显式转换 io.Writer(os.Stdout) 等价。接口的动态类型会设置为指针类型 *os.File 的类型描述符,它的动态值会设置为 os.Stdout 的副本,即一个指向代表进程的标准输出的 os.File 类型的指针。

调用 w.Write([]byte(“hello”)) //
等价于 os.Stdout.Write([]byte("hello"))

接口值可以用 == 和 != 操作符来比较。

在比较两个接口值时,如果两个接口值的动态类型一致,但对应的动态值是不可比较的(如 slice),那么这个比较会导致宕机。

var w io.Writer
fmt.Printf("%T\n", w) // "<nil>"w = os.Stdout
fmt.Printf("%T\n", w) // "*os.File"w = new(bytes.Buffer)
fmt.Printf("%T\n", w) // "*bytes.Buffer"

注意:含有空指针的非空接口
一个接口值是否是 nil 取决于它的动态类型。

const debug = truefunc f(out io.Writer) {if out != nil {out.Write([]byte("done!\n"))}
}
func main() {var buf *bytes.Bufferif debug {buf = new(bytes.Buffer) // 启用输出收集}f(buf)
}

当 main 调用 f 时,把一个类型为*bytes.Buffer 的空指针赋给了 out 参数,所以 out 的动态类型是 *bytes.Buffer,动态值是 nil。但是判断 out != nil 判断的是它的动态类型,所以 out != nil 返回true。
调用 out.Write 时,不允许接收者为空。

正确方法:

var buf io.Writer
if debug {buf = new(bytes.Buffer)
}

7.6 使用 sort.Interface 来排序

package sort
type Interface interface {Len() intLess(i, j int) bool // i, j 是序列元素的下标Swap(i, j int)
}

要对序列排序,需要先确定一个实现了如上三个方法的类型,接着把 sort.Sort 函数应用到上面这类方法的实例上。

type StringSlice []string
func (p StringSlice) Len() int { return len(p)}
func (p StringSlice) Less(i, j int) bool {return p[i] < p[j]}
func (p StringSlice) Swap(i, j int) {p[i], p[j] = p[j], p[i]}
sort.Sort(StringSlice(names))

7.7 http.Handler 函数

package http
type Handler interface {ServeHTTP(w ResponseWriter, r *Request)
}func ListenAndServe(address string, h Handler) error

net/http 包提供了一个请求多工转发器 ServeMux。

下面的代码中,将 /list, price 这样的 URL 和对应的处理程序关联起来。

package mainimport ("fmt""log""net/http"
)func main() {db := database{"shoes": 50, "socks": 5}mux := http.NewServeMux()//!+mainmux.HandleFunc("/list", db.list)mux.HandleFunc("/price", db.price)//!-mainlog.Fatal(http.ListenAndServe("localhost:8000", mux))
}type database map[string]intfunc (db database) list(w http.ResponseWriter, req *http.Request) {for item, price := range db {fmt.Fprintf(w, "%s: $%d\n", item, price)}
}func (db database) price(w http.ResponseWriter, req *http.Request) {item := req.URL.Query().Get("item")if price, ok := db[item]; ok {fmt.Fprintf(w, "$%d\n", price)} else {w.WriteHeader(http.StatusNotFound) // 404fmt.Fprintf(w, "no such item: %q\n", item)}
}

7.8 error 接口

Error 是一个接口类型,包含返回错误消息的方法

type error interface {Error() string
}func New(text string) error { return &errorString{text}}type errorString struct {text string}func (e *errorString) Error() string { return e.Text }

直接调用 errors.New 比较罕见,一般使用 fmt.Errorf。

package jmtimport "errors"func Errorf(format string, args ...interface{}) error {return errors.New(Sprintf(format, args...))
}

7.10 断言类型

类型断言是一个作用在接口值上的操作,写出来类似 x.(T), 其中 x 是一个接口类型的表达式,而 T 是一个断言类型。类型断言会检查作为操作数的动态类型是否满足指定的断言类型。

if f, ok := w.(*os.File); ok {// 使用 f
}

7.11 使用断言类型来识别错误

os.PathError

type PathError struct {Op stringPath stringErr error
}func (e *PathError) Error() string {return e.Op + " " + ": " + e.Err.Error()
}

可以根据类型断言来检查错误的特定类型,这些类型包含的细节远远多于一个简单的字符串。

_, err := os.Open("/no/such/file")
fmt.Println(err)
fmt.Printf("%#v\n", err)

7.12 通过接口类型断言来查询特性

func writeString(w io.Writer, s string)(n int, err error) {type stringWriter interface {WritesString(string) (n int, err error)}if sw, ok := w.(StringWriter); ok {return sw.WriteString(s) // 避免了内存复制}return w.Write([]byte(s))
}

7.13 类型分支

类型分支与普通的分支语句类似,差别是操作数改为 x.(type).

switch x.(type) {case nil:  //case init, unit: //case bool: //
}

相关文章:

Go语言程序设计-第7章--接口

Go语言程序设计-第7章–接口 接口类型是对其他类型行为的概括与抽象。 Go 语言的接口的独特之处在于它是隐式实现。对于一个具体的类型&#xff0c;无须声明它实现了哪些接口&#xff0c;只要提供接口所必须实现的方法即可。 7.1 接口即约定 7.2 接口类型 package iotype …...

性能优化-OpenMP基础教程(二)

本文主要介绍OpenMP并行编程技术&#xff0c;编程模型、指令和函数的介绍、以及OpenMP实战的几个例子。希望给OpenMP并行编程者提供指导。 &#x1f3ac;个人简介&#xff1a;一个全栈工程师的升级之路&#xff01; &#x1f4cb;个人专栏&#xff1a;高性能&#xff08;HPC&am…...

让电脑变得更聪明——用python实现五子棋游戏

作为经典的棋类游戏&#xff0c;五子棋深受大众喜爱&#xff0c;但如果仅实现人与人的博弈&#xff0c;那程序很简单&#xff0c;如果要实现人机对战&#xff0c;教会计算机如何战胜人类&#xff0c;那就不是十分容易的事了。本文我们先从简单入手&#xff0c;完成五子棋游戏的…...

C#-接口

接口 (interface) 定义了一个可由类和结构实现的协定。接口可以包含方法、属性、事件和索引器。接口不提供它所定义的成员的实现 — 它仅指定实现该接口的类或结构必须提供的成员。 接口可支持多重继承。在下面的示例中,接口 IComboBox 同时从 ITextBox 和 IListBox 继承。 i…...

ASP.NET可视化流程设计器源码

源码介绍: ASP.NET可视化流程设计器源码已应用于众多大型企事业单位。拥有全浏览器兼容的可视化流程设计器、表单设计器、基于角色的权限管理等系统开发必须功能&#xff0c;大大为您节省开发时间&#xff0c;是您开发OA.CRM、HR等企事业各种应用管理系统和工作流系统的最佳基…...

景联文科技GPT教育题库:AI教育大模型的强大数据引擎

GPT-4发布后&#xff0c;美国奥数队总教练、卡耐基梅隆大学数学系教授罗博认为&#xff0c;这个几乎是用“刷题”方式喂大的AI教育大模型的到来&#xff0c;意味着人类的刷题时代即将退出历史舞台。 未来教育将更加注重学生的个性化需求和多元化发展&#xff0c;借助GPT和AI教育…...

PHP进阶-实现网站的QQ授权登录

授权登录是站点开发常见的应用场景&#xff0c;通过社交媒体一键授权可以跳过注册站点账户的繁琐操作。本文将讲解如何用PHP实现QQ授权登录。首先&#xff0c;我们需要申请QQ互联开发者账号获得APPID和密钥&#xff1b;接着&#xff0c;我们下载QQ官方SDK&#xff1a;PHP SDK v…...

字节跳动基础架构SRE-Copilot获得2023 CCF国际AIOps挑战赛冠军

近日&#xff0c;2023 CCF国际AIOps挑战赛决赛暨“大模型时代的AIOps”研讨会在北京成功举办&#xff0c;活动吸引了来自互联网、运营商、科研院所、高校、软硬件厂商等领域多名专家学者参与&#xff0c;为智能运维的前沿学术研究、落地生产实践打开了新思路。决赛中&#xff0…...

python moviepy 图文批量合成带字幕口播视频

最近在研究将图片和文本批量合成为带字幕口播视频 主要是基于python的moviepy库 from generator import audio, pics, subs, videodef main():texts_input examplepics_input example# 图片分辨率预处理pics.adjust(pics_input)# 文字转语音audio.text_to_audio(texts_inpu…...

【代码片段】Linux C++打印当前函数调用堆栈

在开发大型项目时&#xff0c;尤其是多线程情况下&#xff0c;一般无法使用断点调试&#xff0c;这时候将当前函数的调用堆栈打印出来是非常有必要和有效的问题排查手段。 这里记录一段Linux环境下&#xff0c;打印函数堆栈的代码。 void get_native_callstack(std::string &a…...

Linux程序、进程以及计划任务(第一部分)

目录 一、程序和进程 1、什么是程序&#xff1f; 2、什么是进程&#xff1f; 3、线程是什么&#xff1f; 4、如何查看是多线程还是单线程 5、进程结束的两种情况&#xff1a; 6、进程的状态 二、查看进程信息的相关命令 1、ps&#xff1a;查看静态的进程统计信息 2、…...

Oracle database 12cRAC异地恢复至单机

环境 rac 环境 byoradbrac Oracle12.1.0.2 系统版本&#xff1a;Red Hat Enterprise Linux Server release 6.5 软件版本&#xff1a;Oracle Database 12c Enterprise Edition Release 12.1.0.2.0 - 64bit byoradb1&#xff1a;172.17.38.44 byoradb2&#xff1a;172.17.38.4…...

【docker】linux部署docker

简介 首先我需要声明的是&#xff0c;我的系统是centos7&#xff0c;下载工具使用的是yum&#xff1b;在linux上部署docker&#xff0c;之前一直看的是这篇文章Linux之Docker部署&#xff0c;基本上功能方面也都可以使用&#xff0c;部署起来也是比较的简单。首先我先讲述这篇…...

【K8S 云原生】Pod资源限制、Pod容器健康检查(探针)

目录 一、docker的重启方式和K8S重启方式 1、Pod的重启方式&#xff1a; 2、docker的重启策略&#xff1a; 二、yaml文件快速生成&#xff1a; 三、pod的状态&#xff1a; 四、Pod的资源限制 1、限制的方式和种类 2、CPU的限制的格式&#xff1a; 五、K8S拉取镜像的策…...

Python从入门到网络爬虫(模块详解)

模块 我们知道&#xff0c;函数和类都是可以重复调用的代码块。在程序中使用位于不同文件的代码块的方法是&#xff1a;导入 (import) 该对象所在的模块 (mudule)。当程序变得越来越大时&#xff0c;将程序的不同部分根据不同分类方法保存在不同文件中通常会更加方便。 导入模…...

[大厂实践] 无停机迁移大规模关键流量(下)

在系统升级、迁移的过程中&#xff0c;如何验证系统逻辑、性能正确无误&#xff0c;是一个很大的挑战。这一系列介绍了Netflix通过重放流量测试解决这一挑战的实践。原文: Migrating Critical Traffic At Scale with No Downtime — Part 2 想象一下&#xff0c;你被心爱的Netf…...

VMware Workstation虚拟机CentOS 7.9 配置固定ip的步骤

VMware Workstation虚拟机CentOS7.9配置固定ip的步骤 编辑虚拟机 打开VMware Workstation。 选择要配置的虚拟机&#xff0c;但不要启动它。 点击“编辑虚拟机设置”&#xff08;Edit virtual machine settings&#xff09;。 选择“网络适配器”&#xff08;Network Adapter&…...

构建自己的私人GPT

创作不易&#xff0c;请大家多鼓励支持。 在现实生活中&#xff0c;很多人的资料是不愿意公布在互联网上的&#xff0c;但是我们又要使用人工智能的能力帮我们处理文件、做决策、执行命令那怎么办呢&#xff1f;于是我们构建自己或公司的私人GPT变得非常重要。 一、本地部署…...

EtherCAT主站SOEM -- 14 --Qt-Soem通过界面采集从站IO进行显示

EtherCAT主站SOEM -- 14 --Qt-Soem通过界面采集从站IO进行显示 一 mainwindow.c 文件函数:1.1 自定义PDO配置1.2 主站初始化二 motrorcontrol.c 文件三 allvalue.h 文件该文档修改记录:总结一 mainwindow.c 文件函数: 1.1 自定义PDO配置 int IO_setup(uint16 slave) {int...

线程安全、共享变量的可见性

Java中的线程安全问题 谈到线程安全问题&#xff0c;我们先说说什么是共享资源。所谓共享资源&#xff0c;就是说该资源被多个线程所持有或者说多个线程都可以去访问该资源。 线程安全问题是指当多个线程同时读写一个共享资源并且没有任何同步措施时&#xff0c;导致出现脏数…...

XCTF-web-easyupload

试了试php&#xff0c;php7&#xff0c;pht&#xff0c;phtml等&#xff0c;都没有用 尝试.user.ini 抓包修改将.user.ini修改为jpg图片 在上传一个123.jpg 用蚁剑连接&#xff0c;得到flag...

内存分配函数malloc kmalloc vmalloc

内存分配函数malloc kmalloc vmalloc malloc实现步骤: 1)请求大小调整:首先,malloc 需要调整用户请求的大小,以适应内部数据结构(例如,可能需要存储额外的元数据)。通常,这包括对齐调整,确保分配的内存地址满足特定硬件要求(如对齐到8字节或16字节边界)。 2)空闲…...

简易版抽奖活动的设计技术方案

1.前言 本技术方案旨在设计一套完整且可靠的抽奖活动逻辑,确保抽奖活动能够公平、公正、公开地进行,同时满足高并发访问、数据安全存储与高效处理等需求,为用户提供流畅的抽奖体验,助力业务顺利开展。本方案将涵盖抽奖活动的整体架构设计、核心流程逻辑、关键功能实现以及…...

基于数字孪生的水厂可视化平台建设:架构与实践

分享大纲&#xff1a; 1、数字孪生水厂可视化平台建设背景 2、数字孪生水厂可视化平台建设架构 3、数字孪生水厂可视化平台建设成效 近几年&#xff0c;数字孪生水厂的建设开展的如火如荼。作为提升水厂管理效率、优化资源的调度手段&#xff0c;基于数字孪生的水厂可视化平台的…...

Rust 异步编程

Rust 异步编程 引言 Rust 是一种系统编程语言,以其高性能、安全性以及零成本抽象而著称。在多核处理器成为主流的今天,异步编程成为了一种提高应用性能、优化资源利用的有效手段。本文将深入探讨 Rust 异步编程的核心概念、常用库以及最佳实践。 异步编程基础 什么是异步…...

select、poll、epoll 与 Reactor 模式

在高并发网络编程领域&#xff0c;高效处理大量连接和 I/O 事件是系统性能的关键。select、poll、epoll 作为 I/O 多路复用技术的代表&#xff0c;以及基于它们实现的 Reactor 模式&#xff0c;为开发者提供了强大的工具。本文将深入探讨这些技术的底层原理、优缺点。​ 一、I…...

html-<abbr> 缩写或首字母缩略词

定义与作用 <abbr> 标签用于表示缩写或首字母缩略词&#xff0c;它可以帮助用户更好地理解缩写的含义&#xff0c;尤其是对于那些不熟悉该缩写的用户。 title 属性的内容提供了缩写的详细说明。当用户将鼠标悬停在缩写上时&#xff0c;会显示一个提示框。 示例&#x…...

A2A JS SDK 完整教程:快速入门指南

目录 什么是 A2A JS SDK?A2A JS 安装与设置A2A JS 核心概念创建你的第一个 A2A JS 代理A2A JS 服务端开发A2A JS 客户端使用A2A JS 高级特性A2A JS 最佳实践A2A JS 故障排除 什么是 A2A JS SDK? A2A JS SDK 是一个专为 JavaScript/TypeScript 开发者设计的强大库&#xff…...

08. C#入门系列【类的基本概念】:开启编程世界的奇妙冒险

C#入门系列【类的基本概念】&#xff1a;开启编程世界的奇妙冒险 嘿&#xff0c;各位编程小白探险家&#xff01;欢迎来到 C# 的奇幻大陆&#xff01;今天咱们要深入探索这片大陆上至关重要的 “建筑”—— 类&#xff01;别害怕&#xff0c;跟着我&#xff0c;保准让你轻松搞…...

宇树科技,改名了!

提到国内具身智能和机器人领域的代表企业&#xff0c;那宇树科技&#xff08;Unitree&#xff09;必须名列其榜。 最近&#xff0c;宇树科技的一项新变动消息在业界引发了不少关注和讨论&#xff0c;即&#xff1a; 宇树向其合作伙伴发布了一封公司名称变更函称&#xff0c;因…...