[每周一更]-(第34期):Go泛型的理解和使用

在这里插入图片描述

泛型的实现方式有很多种,Go 1.18版本引入的泛型实现方式是通过type参数实现的。

在之前的Go版本中,由于语言本身的限制,泛型一直是Go语言的一个痛点和缺陷。程序员通常会使用接口和类型断言来实现泛型,但这种方法有时会导致代码难以理解、调试和维护。

为了解决这个问题,Go 1.18版本引入了泛型机制,允许使用类似类型参数的方式来编写泛型代码。

通过泛型,Go语言中的常见数据结构和算法,例如容器、集合、排序、搜索等,可以被写成通用的代码,同时保持代码的简洁和可读性。

这不仅提高了代码的可重用性和可维护性,还可以减少程序员的工作量,提高编码效率。

需要注意的是,泛型虽然是很有用的编程特性,但也会带来额外的开销,例如编译时的类型检查和运行时的类型转换等。

因此,Go语言在引入泛型机制时,也需要权衡其性能开销和代码可维护性之间的平衡,以实现最佳的编程效率和代码质量。

1、什么是泛型?

泛型是一种通用的编程模式,可以让程序员编写更具通用性和灵活性的代码,同时减少重复的代码和提高代码的可读性。在编程中,泛型是一种用于处理类型的机制,能够让代码在不知道具体类型的情况下完成一些操作,提高代码的重用性和可维护性。

2、泛型可以提供哪块的效率?

泛型的使用可以提供很多方面的效率,包括:

  • 减少代码冗余度。泛型可以将一些通用的算法和数据结构抽象出来,使它们适用于不同的类型,从而减少代码的冗余度。
  • 提高代码的可读性。泛型可以使代码更加简洁,易于理解和维护。
  • 提高代码的可重用性。通过泛型,可以编写更通用的代码,可以在不同的场合进行复用,提高了代码的可重用性。
  • 提高编码效率。使用泛型可以让程序员在编写代码时更加高效,减少了大量的重复工作。

3、Go语言中泛型如何使用?

在之前的Go版本中,由于语言本身的限制,泛型一直是Go语言的一个痛点和缺陷。程序员通常会使用接口和类型断言来实现泛型,但这种方法有时会导致代码难以理解、调试和维护。为了解决这个问题,Go 1.18版本引入了泛型机制,允许使用类似类型参数的方式来编写泛型代码。

在Go语言中,泛型使用type参数实现。在函数或方法中,可以定义一个类型参数,用于表示待处理的具体类型。例如:

func Swap[T any](a, b *T) {
    tmp := *a
    *a = *b
    *b = tmp
}

在这个例子中,我们定义了一个名为T的类型参数,使用了any关键字限制其类型。在函数内部,我们可以使用T来表示任何类型的变量,从而实现了通用的交换两个变量的值的函数。调用这个函数时,可以传入任何类型的变量。

除了函数,泛型也可以在结构体、接口和方法中使用。例如:

type Stack[T any] struct {
    data []T
}

func (s *Stack[T]) Push(x T) {
    s.data = append(s.data, x)
}

func (s *Stack[T]) Pop() T {
    x := s.data[len(s.data)-1]
    s.data = s.data[:len(s.data)-1]
    return x
}

在这个例子中,我们定义了一个名为Stack的结构体,使用了类型参数T。这个结构体代表一个通用的

4、泛型的一些特殊的使用场景

除了上述基本使用方式,泛型还有一些特殊的使用场景,这些场景下泛型的能力会被更加充分地发挥。

(1)类型约束

在某些情况下,我们需要对类型进行约束,只允许某些类型作为泛型类型参数,这时可以使用类型约束。Go语言中,可以使用接口实现类型约束,比如:

type Stringer interface {
    String() string
}

func ToString[T Stringer](slice []T) []string {
    result := make([]string, len(slice))
    for i, val := range slice {
        result[i] = val.String()
    }
    return result
}

上面的例子中,定义了一个Stringer接口,约束了只有实现了该接口的类型才能作为ToString函数的类型参数。这样就可以避免一些类型不支持String方法的情况。

(2)函数类型作为泛型类型参数

在某些情况下,我们需要将函数类型作为泛型类型参数,这时可以使用函数类型作为类型参数。比如:

func Map[T any, R any](slice []T, f func(T) R) []R {
    result := make([]R, len(slice))
    for i, val := range slice {
        result[i] = f(val)
    }
    return result
}

上面的例子中,使用了函数类型作为类型参数,这样就可以在函数内部调用传入的函数参数f。

(3)自定义泛型类型

有时候,标准库中提供的泛型类型不能满足我们的需求,这时可以通过自定义泛型类型来实现。自定义泛型类型可以通过结构体、接口等方式实现。比如:

type Stack[T any] struct {
    data []T
}

func (s *Stack[T]) Push(val T) {
    s.data = append(s.data, val)
}

func (s *Stack[T]) Pop() T {
    if len(s.data) == 0 {
        panic("stack is empty")
    }
    val := s.data[len(s.data)-1]
    s.data = s.data[:len(s.data)-1]
    return val
}

上面的例子中,定义了一个Stack类型,支持任意类型的元素。通过在类型参数位置加上any关键字,实现了泛型类型的定义。

总之,泛型是一种非常强大的编程技术,可以提高代码的复用性和可读性。虽然Go语言在泛型方面不如其他一些语言,但是随着Go语言版本的不断更新和改进,泛型的支持也在不断增强,相信在不久的将来,Go语言的泛型将会更加完善和强大。

实现泛型的其他方式

在Go语言中,1.18前还没有原生支持泛型的语法,不过可以使用一些技巧来实现类似泛型的功能。下面介绍几种常用的方式。

下面是一些在Go语言中使用泛型的例子,其中使用了上述介绍的三种方式中的一种或多种。

(1)使用interface{}作为类型参数

在这个例子中,我们实现了一个函数Find,它可以在一个切片中查找指定元素的位置。这个函数使用了interface{}类型作为泛型类型参数,可以接受任意类型的切片和元素,并使用T类型的运算符==来进行比较操作。

func Find[T comparable](arr []T, x T) int {
    for i, val := range arr {
        if val == x {
            return i
        }
    }
    return -1
}

func main() {
    arr1 := []int{1, 2, 3, 4, 5}
    arr2 := []string{"a", "b", "c", "d", "e"}
    fmt.Println(Find(arr1, 3)) // output: 2
    fmt.Println(Find(arr2, "d")) // output: 3
}

(2)使用反射实现泛型

在这个例子中,我们实现了一个函数ToString,它可以将一个任意类型的切片转换为一个字符串类型的切片。这个函数使用了interface{}类型作为泛型类型参数,然后使用反射机制获取slice的值和类型信息,并使用类型转换和类型断言来完成类型的转换和操作。

func ToString(slice interface{}) []string {
    v := reflect.ValueOf(slice)
    if v.Kind() != reflect.Slice {
        panic("not a slice")
    }
    result := make([]string, v.Len())
    
    
    for i := 0; i < v.Len(); i++ {
        result[i] = fmt.Sprintf("%v", v.Index(i))
    }
    return result
}

func main() {
    arr1 := []int{1, 2, 3, 4, 5}
    arr2 := []string{"a", "b", "c", "d", "e"}
    fmt.Println(ToString(arr1)) // output: [1 2 3 4 5]
    fmt.Println(ToString(arr2)) // output: [a b c d e]
}

(3)使用代码生成工具实现泛型

在这个例子中,我们使用go generate工具来生成一个根据指定类型生成指定大小的数组的代码。首先,我们编写一个模板代码,包含了我们要生成的代码。然后,我们使用go generate命令和一个类型参数来生成代码。最后,我们可以使用生成的代码来创建一个指定大小的数组。

//go:generate go run gen_array.go T=int N=10
//go:generate go run gen_array.go T=string N=5

package main

import "fmt"

func main() {
    arr1 := make([]int, 10)
    arr2 := make([]string, 5)
    fmt.Println(len(arr1), len(arr2)) // output: 10 5
}

在上面的例子中,我们定义了一个模板代码gen_array.go,它包含了一个名为Array的结构体和一个名为NewArray的函数,用于生成指定大小的
除了使用泛型函数和泛型接口外,Go语言中还有一些其他的泛型使用场景,例如泛型容器和类型参数化。

泛型容器指的是一类能够存储任意类型数据的容器。在传统的非泛型语言中,需要为每一种数据类型都定义一个对应的容器,而泛型容器可以通过参数化类型实现同一种容器适用于不同的数据类型。在Go语言中,可以使用interface{}类型实现泛型容器。例如,下面的代码定义了一个Stack结构体,可以用于存储任意类型的数据:

type Stack struct {
    elems []interface{}
}

func (s *Stack) Push(elem interface{}) {
    s.elems = append(s.elems, elem)
}

func (s *Stack) Pop() interface{} {
    if len(s.elems) == 0 {
        return nil
    }
    elem := s.elems[len(s.elems)-1]
    s.elems = s.elems[:len(s.elems)-1]
    return elem
}

在上面的代码中,Stack结构体中的elems字段是一个interface{}类型的切片,可以存储任意类型的数据。Push方法和Pop方法可以分别用于向Stack中添加元素和弹出元素。

类型参数化指的是一种将类型参数化的技术。在Go语言中,可以使用type关键字定义一个类型别名,然后在函数或接口中使用这个类型别名作为参数来实现类型参数化。例如,下面的代码定义了一个泛型函数Max,可以比较任意类型的值并返回最大值:

type Ordering int

const (
    Less Ordering = iota - 1
    Equal
    Greater
)

func Max[T comparable](a, b T) T {
    switch a := compare(a, b); a {
    case Greater:
        return a
    }
    return b
}

func compare[T comparable](a, b T) Ordering {
    switch {
    case a < b:
        return Less
    case a > b:
        return Greater
    }
    return Equal
}

在上面的代码中,Max函数使用了类型参数T,并在参数列表中使用了comparable关键字来限制T只能是可比较的类型。在函数体中,调用了另一个泛型函数compare来比较a和b的大小,并根据比较结果返回最大值。

通过泛型容器和类型参数化,Go语言中的泛型可以实现更加灵活和通用的编程,提高代码的复用性和可维护性。不过需要注意的是,Go语言中的泛型并不支持隐式类型转换和动态类型,需要开发者在编写代码时做好类型检查和类型转换的处理。

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

哪些行业装配柔性线路板 (FPC)时用到UV胶水?

柔性线路板&#xff08;FPC&#xff09;因其可弯曲和轻薄的特性&#xff0c;在多种行业和应用中都有广泛的应用。以下是一些常见的行业和场景&#xff0c;它们在装配FPC时可能使用UV胶水&#xff1a; 1.电子产品制造&#xff1a; 移动设备&#xff1a;如智能手机、平板电脑和可…

在线短信变量批量编辑工具

1.打开网站 短信编辑工具 2.点击:下载模板 3.编辑后 导入表格 4.点击合成短信 5.选中后复制 ctrlc 初衷&#xff1a; 短信变量批量编辑工具是一种用于批量编辑短信变量的工具。短信变量指的是短信中的可变部分&#xff0c;比如姓名、日期、金额等内容。 使用这个工具&…

python学习笔记--异常捕获

异常场景 numinput("input you number:") n9000 try:resultn/int(num)print({} 除以num 结果为{}.format(n,result)) except ZeroDivisionError as err:print("0不可以作为除数&#xff0c;出现报错{}".format(err)) except ValueError as err:print(&quo…

基于物联网云平台的大棚温湿度监控系统

我国是农业大国&#xff0c;农业生产的质量对我国具有非常重要的意义&#xff0c;受到数字化技术的重点支持&#xff0c;但仍未完全实现智能化&#xff0c;农业生产效率仍有提升空间。 將物联网云服务应用到农业环境的监控中来&#xff0c;可以更高效地管理环境状况。贝尔智能…

Jmeter压力测试简单教程(包括服务器状态监控)

前段时间公司需要对服务器进行压力测试&#xff0c;包括登录前的页面和登录后的页面&#xff0c;主要目的是测试负载均衡的实现效果。不知道是不是因为Jmeter不如loadRunner火爆还是什么&#xff0c;网上关于Jmeter的资料有很多但是大多千篇一律&#xff0c;要么简单弄个页面测…

数据结构学习 leetcode64最小路径和

动态规划 题目&#xff1a; 建议看这里&#xff0c;有这道题详细的解析。我觉得写的挺好。 这是我在学动态规划的时候&#xff0c;动手做的一道题。 虽然我在学动态规划&#xff0c;但是我之前学了dps&#xff0c;所以我就想先用dps试着做&#xff0c;结果发现不行&#xf…

百川大模型AI对话实战——Python开发一个对话机器人

百川大模型开放提供API体验中心&#xff0c;体验不错&#xff0c;有小伙伴也对搭建自己的对话机器人比较兴趣&#xff0c;今天通过Python来简单介绍下&#xff0c;如何调用百川大模型的API来构建自己的小产品。 在开发环境中安装Python&#xff0c;如何安装&#xff1f;参照网…

全网最细,Jmeter性能测试-入门级接口压测思路,一文打通...

目录&#xff1a;导读 前言一、Python编程入门到精通二、接口自动化项目实战三、Web自动化项目实战四、App自动化项目实战五、一线大厂简历六、测试开发DevOps体系七、常用自动化测试工具八、JMeter性能测试九、总结&#xff08;尾部小惊喜&#xff09; 前言 1、压力测试重点关…

Nodejs 第三十章(防盗链)

防盗链&#xff08;Hotlinking&#xff09;是指在网页或其他网络资源中&#xff0c;通过直接链接到其他网站上的图片、视频或其他媒体文件&#xff0c;从而显示在自己的网页上。这种行为通常会给被链接的网站带来额外的带宽消耗和资源浪费&#xff0c;而且可能侵犯了原始网站的…

整数比较1 C语言xdoj93

描述&#xff1a; 编写程序&#xff0c;对于从键盘输入的2个整数&#xff0c;先输出较大者的个位数字&#xff0c;然后输出较小者的平方值。 输入说明&#xff1a; 输入的两个整数之间以一个空格分隔。 输出说明&#xff1a; 在一行上输出两个整数&#xff0c;整数之间以一个空…

将qt程序注册成服务

将qt程序注册成服务 1、qt服务模块管理下载 qt-solutions 2、QtService项目 2.1、将qtservice拷贝到项目代码路径 2.2、实现服务管理 PS&#xff1a;响应服务的启停 CustomService.h #include <QCoreApplication> #include "qtservice.h"class CustomSer…

上市公司-客户、供应商集中度(2000-2022年)

参考《中国工业经济》中吴安兵&#xff08;2023&#xff09;、《上海财经大学学报》中邱保印&#xff08;2023&#xff09;的做法&#xff0c;以客户集中度和供应商集中度之和衡量企业供应链集中度 其中客户集中度以前五名客户产生的营业收入占比衡量&#xff0c;供应商集中度…

好物设计- 实现区域图片变化自动截图

工具–Py即可 重点怎么获取窗口句柄? 使用 spyxx 可以获得句柄 (相当一个窗口的ID,无论窗口怎么变化ID不变我们都可以找到该窗口的详细信息) 替换句柄就可以,也可以不用句柄之间改截图区域 实战图片 import pygetwindow as gw import pyautogui import time import numpy a…

工业交换机之间Profinet无线以太网通信

在实际应用中&#xff0c;车间里控制柜内会有PLC、伺服电机、变频器等设备同时与触摸屏做数据交互&#xff0c;这些设备一般通过工业交换机进行数据组网。总控室内的PC组态软件往往需要采集到&#xff0c;车间内各部分触摸屏、PLC、变频器等设备信号&#xff0c;此时往往是工业…

当代大学生应该如何学习计算机科学

我相信&#xff0c;看到这个标题并且愿意阅读往下阅读的你&#xff0c;一定是正在学习计算机&#xff0c;而自己感到迷茫&#xff0c;或者你还真在考虑要不要学习计算机科学&#xff0c;再或者你是想学计算机而不知道到底该怎么去学的&#xff0c;好&#xff0c;既然你是榜上有…

ssm基于JAVA的校园综合服务系统论文

摘 要 信息数据从传统到当代&#xff0c;是一直在变革当中&#xff0c;突如其来的互联网让传统的信息管理看到了革命性的曙光&#xff0c;因为传统信息管理从时效性&#xff0c;还是安全性&#xff0c;还是可操作性等各个方面来讲&#xff0c;遇到了互联网时代才发现能补上自古…

代码随想录第三十六天(一刷C语言)|背包问题理论基础分割等和子集

创作目的&#xff1a;为了方便自己后续复习重点&#xff0c;以及养成写博客的习惯。 一、背包问题 题目&#xff1a;有n件物品和一个最多能背重量为w 的背包。第i件物品的重量是weight[i]&#xff0c;得到的价值是value[i] 。每件物品只能用一次&#xff0c;求解将哪些物品装…

【C#】.net core 6.0 通过依赖注入注册和使用上下文服务

给自己一个目标&#xff0c;然后坚持一段时间&#xff0c;总会有收获和感悟&#xff01; 请求上下文是指在 Web 应用程序中处理请求时&#xff0c;包含有关当前请求的各种信息的对象。这些信息包括请求的头部、身体、查询字符串、路由数据、用户身份验证信息以及其他与请求相关…

Android 自动适配屏幕方案—— smallestWidth

smallestWidth限定符适配原理和屏幕分辨率限定符适配一样&#xff0c;都是通过创建多个values文件夹&#xff0c;系统根据限定符去寻找对应的dimens.xml文件&#xff0c;以确定不同设备上的大小展示&#xff0c;smallestWidth 限定符适配是拿 dp 值来等比缩放. 如何使用 一、…

低代码和纯代码:双向奔赴,共创未来ing……

低代码开发是近年来迅速崛起的软件开发方法&#xff0c;让编写应用程序变得更快、更简单。有人说它是美味的膳食&#xff0c;让开发过程高效而满足&#xff0c;但也有人质疑它是垃圾食品&#xff0c;缺乏定制性与深度。你认为低代码到底是美味的膳食还是垃圾食品呢&#xff0c;…