golang中的Interface接口 类型断言、接口赋值、空接口的使用、接口嵌套

news2024/11/16 17:51:51

Interface整理

文章目录

  • Interface整理
    • 接口嵌套接口
    • 类型断言
    • 类型判断 type-switch
    • 使用方法集与接口
    • 空接口
      • 实例
    • 接口赋值给接口

接口是一种契约,实现类型必须满足它,它描述了类型的行为,规定类型可以做什么。接口彻底将类型能做什么,以及如何做分离开来,使得相同接口的变量在不同的时刻表现出不同的行为,这就是多态的本质。

编写参数是接口变量的函数,这使得它们更具有一般性。

使用接口使代码更具有普适性。

最近在学Go当中的接口,学的有点云里雾里 ,这个interface和Java的也太不像了,我们先来看看Java当中的接口是怎么用的:

首先我们先定义一个接口:

public interface Study {    //使用interface表示这是一个接口
    void study();    //接口中只能定义访问权限为public抽象方法,其中public和abstract关键字可以省略
}

之后我们用关键字继承:

public class Student extends Person implements Study {   //使用implements关键字来实现接口
    public Student(String name, int age, String sex) {
        super(name, age, sex, "学生");
    }

    @Override
    public void study() {    //实现接口时,同样需要将接口中所有的抽象方法全部实现
        System.out.println("我会学习!");
    }
}

public class Teacher extends Person implements Study {
    protected Teacher(String name, int age, String sex) {
        super(name, age, sex, "教师");
    }

    @Override
    public void study() {
        System.out.println("我会加倍学习!");
    }

这样一个显示继承的方式非常清晰明了,接下来看看Go里面的接口:

type Namer interface {
    Method1(param_list) return_type
    Method2(param_list) return_type
    ...
}

这样一看没有什么很大的区别,都需要先声明一个接口但是不使用,接下来看看接口的实现:

package main

import "fmt"

type Shaper interface {
	Area() float32
}

type Square struct {
	side float32
}

func (sq *Square) Area() float32 {
	return sq.side * sq.side
}

func main() {
	sq1 := new(Square)
	sq1.side = 5

	var areaIntf Shaper
	areaIntf = sq1
	// shorter,without separate declaration:
	// areaIntf := Shaper(sq1)
	// or even:
	// areaIntf := sq1
	fmt.Printf("The square has area: %f\n", areaIntf.Area())
}

这样就会发现如下几个区别:

  1. 并没有显式继承
  2. 接口能声明变量,并通过该变量指向方法
  3. 实现方法中的参数为自定义的结构体

一个接口类型的变量或一个 接口值

首先我们来看第一点,关于为什么不显示继承,这一点我在网上搜过,观点基本是Go强调的是组合而非继承,并没有一个很确切的理论,那暂且不议

第二点:areaIntf是一个多字(multiword)数据结构,它的值是 nil。接口变量里包含了接收者实例的值和指向对应方法表的指针。

在Go中,我们自定义的结构体就像Java中的类一样,可以实现接口中的方法。我们可以同一个接口被实现多次。当时就有了点疑问:不是不允许函数重载吗?后来发现方法和函数是完全不同的概念:

Go中不允许函数(function)重载是为了提高效率,而方法(method)的可多次实现则体现了Go的多态,也就是根据场景选择。


接下来,我们看一些进阶功能:

接口嵌套接口

在Java 和go当中,我们都倡导一个接口的简洁明了。比如说先定义一个结构体为综测,综测又是由考试成绩、竞赛、体育等等组成,考试成绩里面又有不同科,体育里面也有不同科,这个时候我们就应该分开定义,之后进行嵌套。我个人的理解的理解就是类似于树一样的存在,而一个结构体就是一个父节点。这里还是放一个实例:

type ReadSeeker interface {
	Reader
	Seeker
}

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

type Seeker interface {
	Seek(offset int64, whence int) (int64, error)
}

类型断言

我们通常会想知道一个接口变量里面是什么类型,这个时候我们就会用到类型断言,通用格式为:

typeA := var1.(T)

var1为接口变量,T是想知道的类型。如果转换合法,typeAvar1转换到类型 T 的值

如果在判断式中使用,则是这样的:

if t, ok := areaIntf.(*Square); ok {
		fmt.Printf("The type of areaIntf is: %T\n", t)
}

如果转换合法,t 是 转换到类型的值,ok 会是 true;否则 t是类型的零值,okfalse,也没有运行时错误发生。

注意:如果忽略 areaIntf.(*Square) 中的 * 号,会导致编译错误:impossible type assertion: Square does not implement Shaper (Area method has pointer receiver)

同理,我们也可以判断他是否属于该接口:

type Stringer interface {
    String() string
}

if sv, ok := v.(Stringer); ok {
    fmt.Printf("v implements String(): %s\n", sv.String()) // note: sv, not v
}

类型判断 type-switch

个人认为如果说类型断言是只想知道值是不是某个类型,那么此语句则是想知道究竟是哪个重要的类型或者不需要知道的类型,常见用法如下:

func classifier(items ...interface{}) {
	for i, x := range items {
		switch x.(type) {
		case bool:
			fmt.Printf("Param #%d is a bool\n", i)
		case float64:
			fmt.Printf("Param #%d is a float64\n", i)
		case int, int64:
			fmt.Printf("Param #%d is a int\n", i)
		case nil:
			fmt.Printf("Param #%d is a nil\n", i)
		case string:
			fmt.Printf("Param #%d is a string\n", i)
		default:
			fmt.Printf("Param #%d is unknown\n", i)
		}
	}
}

可以用 type-switch 进行运行时类型分析,但是在 type-switch 不允许有 fallthrough

使用方法集与接口

作用于变量上的方法实际上是不区分变量到底是指针还是值的。当碰到接口类型值时,这会变得有点复杂,原因是接口变量中存储的具体值是不可寻址的,

package main

import (
	"fmt"
)

type List []int

func (l List) Len() int {
	return len(l)
}

func (l *List) Append(val int) {
	*l = append(*l, val)
}

type Appender interface {
	Append(int)
}

func CountInto(a Appender, start, end int) {
	for i := start; i <= end; i++ {
		a.Append(i)
	}
}

type Lener interface {
	Len() int
}

func LongEnough(l Lener) bool {
	return l.Len()*10 > 42
}

func main() {
	// A bare value
	var lst List
	// compiler error:
	// cannot use lst (type List) as type Appender in argument to CountInto:
	//       List does not implement Appender (Append method has pointer receiver)
	
	CountInto(lst, 1, 10) //错误代码 
	if LongEnough(lst) { // VALID: Identical receiver type
		fmt.Printf("- lst is long enough\n")
	}

	// A pointer value
	plst := new(List)
	CountInto(plst, 1, 10) // VALID: Identical receiver type
	if LongEnough(plst) {
		// VALID: a *List can be dereferenced for the receiver
		fmt.Printf("- plst is long enough\n")
	}
}

输出

Untitled

讨论

lst 上调用 CountInto 时会导致一个编译器错误,因为 CountInto 需要一个 Appender,而它的方法 Append 只定义在指针上。 在 lst 上调用 LongEnough 是可以的,因为 Len 定义在值上。

plst 上调用 CountInto 是可以的,因为 CountInto 需要一个 Appender,并且它的方法 Append 定义在指针上。 在 plst 上调用 LongEnough 也是可以的,因为指针会被自动解引用。

总结

在接口上调用方法时,必须有和方法定义时相同的接收者类型或者是可以根据具体类型 P 直接辨识的:

  • 指针方法可以通过指针调用
  • 值方法可以通过值调用
  • 接收者是值的方法可以通过指针调用,因为指针会首先被解引用
  • 接收者是指针的方法不可以通过值调用,因为存储在接口中的值没有地址

将一个值赋值给一个接口时,编译器会确保所有可能的接口方法都可以在此值上被调用,因此不正确的赋值在编译期就会失败。

译注

Go 语言规范定义了接口方法集的调用规则:

  • 类型 T 的可调用方法集包含接受者为 TT 的所有方法集
  • 类型 T 的可调用方法集包含接受者为 T的所有方法
  • 类型 T 的可调用方法集包含接受者为 T 的方法

接下来我们讨论下空接口

空接口

定义:不包含任何方法,对实现没有要求

空接口类似 Java/C# 中所有类的基类: Object 类,二者的目标也很相近。

可以给一个空接口类型的变量 var val interface {} 赋任何类型的值

每个 interface {} 变量在内存中占据两个字长:一个用来存储它包含的类型,另一个用来存储它包含的数据或者指向数据的指针。

这样光看似乎觉得没什么大不了的,我们举个例子,比如说创建树或者其他数据结构,如果我们要根据每个数据类型来定义不同的方法,那无疑是很浪费时间的,这时候就可以用到空接口,实现一键通用:

package main

import (
	"fmt"
)

type Node struct {
	le   *Node
	data interface{}
	rl   *Node
}

func NewNode(left, right *Node) *Node {
	return &Node{left, nil, right}
}

func (n *Node) setData(data interface{}) {
	n.data = data
}

func main() {
	root := NewNode(nil, nil)
	root.setData("root node")
	a := NewNode(nil, nil)
	a.setData("left node")
	b := NewNode(nil, nil)
	b.setData(1)
	root.le = a
	root.rl = b
	fmt.Printf("%v\n", root)
}

实例

我们来看一些实际应用,在GORM框架中,我们创建对象可以使用map的数据结构导入,但是我们无法保证数据都是一个类型,所以就需要一个空接口来帮我们接住所有类型:

db.Model(&User{}).Create([]map[string]interface{}{
  {"Name": "jinzhu_1", "Age": 18},
  {"Name": "jinzhu_2", "Age": 20},
})

接口赋值给接口

一个接口的值可以赋值给另一个接口变量,前提是底层类型实现了必要的方法,此转换是在运行时检查的,转换失败的时候会导致一个运行时错误,这也是GO的动态的一点

比如此代码

package main

import "fmt"

type Shaper interface {
    Area() float64
}

type Square struct {
    side float64
}

func (s Square) Area() float64 {
    return s.side * s.side
}

type Circle struct {
    radius float64
}

func main() {
    var s Shaper
    c := Circle{radius: 5.0}

    // 错误的示例:将接口 Shaper 赋值给接口 Shaper,但底层类型 Circle 并没有实现 Area() 方法
    s = c

    fmt.Printf("Area of the shape: %f\n", s.Area())
}

错误显示:

Untitled

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1152243.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

如何在十亿级别用户中检查用户名是否存在?

不知道大家有没有留意过&#xff0c;在使用一些app注册的时候&#xff0c;提示你用户名已经被占用了&#xff0c;需要更换一个&#xff0c;这是如何实现的呢&#xff1f;你可能想这不是很简单吗&#xff0c;去数据库里查一下有没有不就行了吗&#xff0c;那么假如用户数量很多&…

常用排序算法的理解

1.插入排序 插入排序的思想是将一个记录插入到已经排好序的有序表中&#xff0c;从而形成一个新的、记录数加1的有序表。在其实现过程使用双层循环&#xff0c;外层循环是进行插入的次数&#xff08;也可以理解为比较的轮数&#xff09;&#xff0c;内层循环是当前记录查找插入…

Echats-自定义图表2

效果图&#xff1a; 代码&#xff1a; <!DOCTYPE html> <html lang"zh-cmn-Hans"><head><meta charset"UTF-8" /><meta name"viewport" content"widthdevice-width, initial-scale1.0" /><title>…

iptables的四表五链以及一些应用小场景

一、前言 本文主要学习iptables的一些学习&#xff0c;讲解一些四表五链的基本概念&#xff0c;同时通过iptables实现一下场景&#xff0c;比如反向代理端口、禁用域名、限制IP和端口访问。 二、基本概念 2.1 什么是iptables iptables是Linux的防火墙管理工具而已&#xff0c…

昂利康-002940 三季报分析(20231030)

昂利康-002940 基本面分析 基本情况 公司名称&#xff1a;浙江昂利康制药股份有限公司 A股简称&#xff1a;昂利康 成立日期&#xff1a;2001-12-30 上市日期&#xff1a;2018-10-23 所属行业&#xff1a;医药制造业 周期性&#xff1a;0 主营业务&#xff1a;化学原料药及制剂…

hack_me_please靶机攻略

hack_me_please 扫描 探查无果&#xff0c;扫描js的时候有结果 访问可以看到 该页面可以看到是SeedDMS搭的 应该和CMS类似 渗透 漏洞库查找一下有没有该漏洞 使用whatweb扫描一下刚才的页面 whatweb http://10.4.7.154/seeddms51x/seeddms-5.1.22/ 这个版本高于漏洞库的&a…

软考系统架构师知识点集锦八:嵌入式系统

一、考情分析 二、考点精讲 2.1嵌入式系统概述 2.1.1基本概念 (1)嵌入式系统是以应用为中心、以计算机技术为基础,并将可配置与可裁剪的软、硬件集成于一体的专用计算机系统&#xff0c;需要满足应用对功能、可靠性、成本、体积和功耗等方面的严格要求。 (2)从计算机角度看,嵌…

0039Java程序设计-基于java校园闲置物交易系统论文

文章目录 摘 要目 录系统设计开发环境 摘 要 本文的研究方向是设计和实现学生闲置物网上交易平台。目前&#xff0c;各大高校每年都要举办热热闹闹的“跳蚤”市场&#xff0c;就是给师生的一个闲置物品交易的场所&#xff0c;由此可以看出&#xff0c;大学生对闲置物品交易掉的…

数据结构:算法(特性,时间复杂度,空间复杂度)

目录 1.算法的概念2.算法的特性1.有穷性2.确定性3.可行性4.输入5.输出 3.好算法的特质1.正确性2.可读性3.健壮性4.高效率与低存储需求 4.算法的时间复杂度1.事后统计的问题2.复杂度表示的计算1.加法规则2.乘法规则3.常见函数数量级比较 5.算法的空间复杂度1.程序的内存需求2.例…

CAN总线通信协议

Reference video: 趋近于完美的通讯 CAN总线&#xff01;4分钟看懂&#xff01; CAN通信精华整理&#xff0c;汽车工程师必备技能&#xff0c;一个视频带你轻松掌握&#xff01; 写在前面&#xff1a;CAN通信就三个要点 - 波特率的配置 - 过滤寄存器的配置与理解&#xff08;…

Django 社区志愿者管理系统

摘 要 随着社会的发展&#xff0c;社会的各行各业都在利用信息化时代的优势。计算机的优势和普及使得各种信息系统的开发成为必需。 社区志愿者服务管理系统&#xff0c;主要的模块包括查看首页、个人中心、通知公告管理、志愿者管理、普通管理员管理、志愿活动管理、活动宣…

计算机毕业设计选题推荐-超市售货微信小程序/安卓APP-项目实战

✨作者主页&#xff1a;IT研究室✨ 个人简介&#xff1a;曾从事计算机专业培训教学&#xff0c;擅长Java、Python、微信小程序、Golang、安卓Android等项目实战。接项目定制开发、代码讲解、答辩教学、文档编写、降重等。 ☑文末获取源码☑ 精彩专栏推荐⬇⬇⬇ Java项目 Python…

【C语言初学者周冲刺计划】1.1用筛选法求100之内的素数

目录 1解题思路&#xff1a; 2代码如下&#xff1a; 3运行代码如图所示&#xff1a; 4总结&#xff1a; (前言周冲刺计划:周一一个习题实操&#xff0c;依次类推加一&#xff0c;望各位读者可以独自实践敲代码) 1解题思路&#xff1a; 首先了解筛选法定义&#xff1a;先把…

免费的PPT模版--九五小庞

PPT模板&#xff1a; www.1ppt.com/moban/    行业PPT模板&#xff1a;www.1ppt.com/hangye/ 节日PPT模板&#xff1a;www.1ppt.com/jieri/    PPT素材&#xff1a; www.1ppt.com/sucai/PPT背景图片&#xff1a;www.1ppt.com/beijing/   PPT图表&#xff…

AssertionError: Torch not compiled with CUDA enabled

Pytorch和CUDA版本不兼容&#xff0c;运行python后&#xff08;终端输入python回车&#xff09;用以下代码测试 import torch print(torch.__version__) print(torch.cuda.is_available())返回False则说明目前的pytorch版本无法使用显卡&#xff0c;如下图所示 接着重装合适版…

新能源汽车电池包自动三维尺寸检测系统蓝光光学平面度测量仪-CASAIM

电池包是新能源汽车核心能量源&#xff0c;为整车提供驱动电能。作为新能源汽车的核心部件&#xff0c;其品质直接决定了整车性能。 由于电池包的生产工艺相对复杂&#xff0c;传统的测量工具不仅测量工序复杂、精度不足&#xff0c;还会或多或少接触到电池表面形成瑕疵&#…

[UDS] --- ECUReset 0x11

1 0x11功能描述 根据ISO14119-1标准中所述&#xff0c;诊断服务11主要用于Client向Server(ECU)请求重启行为。该重启行为将会导致Server复位回归到特定的初始状态&#xff0c;具体是什么初始状态取决于Client的请求行为。 2 0x11应用场景 一般而言&#xff0c;对于11诊断服务…

案例分析真题-系统建模

案例分析真题-系统建模 2009年真题 【问题1】 【问题2】 【问题3】 2012年真题 【问题1】 【问题2】 【问题3】 2014年真题 【问题1】 【问题2】 骚戴理解&#xff1a;这个题目以前经常考&#xff0c;不知道今年会不会考&#xff0c;判断的话就是看加工有没有缺少输入和输出&a…

Linux进程的概念

一&#xff1a;冯诺依曼体系结构 什么叫做体系结构&#xff1f;&#xff1f;&#xff1f; 计算机组成 / 芯片架构 输入单元&#xff1a;键盘、话筒、摄像头、usb、鼠标、磁盘&#xff08;ROM&#xff09;/ssd、网卡、显卡 存储器&#xff1a;内存&#xff08;RAM&#xff09…

apache seatunnel支持hive jdbc

上传hive jdbc包HiveJDBC42.jar到seatunel lib安装目录 原因是cloudera 实现了add batch方法 创建seatunnel任务文件mysql2hivejdbc.conf env {execution.parallelism = 2job.mode = "BATCH"checkpoint.interval = 10000 } source {Jdbc {url = "jdbc:mysql:/…