Golang 泛型学习

news2024/11/27 16:29:00

Golang 泛型

今天来学习下Golang中泛型的基础知识。使用泛型,开发者可以声明一个函数来适应不同类型的参数,避免由于参数类型不一致而声明多个处理逻辑类似的函数。在本教程中,将声明两个简单的非泛型函数,然后在单个泛型函数中实现相同的逻辑。

基本要求

  • Go 1.18 及更高版本
  • 合适的编译工具 - text编辑器也满足要求
  • 命令终端 - Linux、Mac系统shell, Windows系统的Cmd、PowerShell

创建目录

新建代码目录

打开shell终端,使用以下命令创建文件夹,作为后续代码案例的根目录

$ mkdir generics
$ cd generics

初始化项目

使用go 初始化命令,对项目进行初始化

$ go mod init example/generics

在这里插入图片描述

项目初始化之后,就可以编写一些简单的示例代码,为了方便对比非泛型函数与泛型函数之间的区别,我们先学习一下非泛型函数使用过程中的缺陷。

非泛型函数

在这个章节中,增加两个函数,每个函数将参数值进行累加并返回。在以下的示例中,由于参数类型的不同(一个是int64类型参数,一个是float64类型参数),开发者必须声明两个函数满足业务的要求(即时函数内部的逻辑处理都一致)

逻辑代码

新增main.go代码文件,代码内容如下

package main

import "fmt"

// SumInts adds together the values of m.
func SumInts(m map[string]int64) int64 {
    var s int64
    for _, v := range m {
        s += v
    }
    return s
}

// SumFloats adds together the values of m.
func SumFloats(m map[string]float64) float64 {
    var s float64
    for _, v := range m {
        s += v
    }
    return s
}

func main() {
    // Initialize a map for the integer values
    ints := map[string]int64{
        "first":  34,
        "second": 12,
    }

    // Initialize a map for the float values
    floats := map[string]float64{
        "first":  35.98,
        "second": 26.99,
    }

    fmt.Printf("Non-Generic Sums: %v and %v\n",
        SumInts(ints),
        SumFloats(floats))
}

执行代码

# 使用以下命令运行代码 可以看到两个函数计算的total值
$ go run main.go

在这里插入图片描述

仔细分析下SumInts、SumFloats函数,其内部的处理逻辑几乎一致,声明变量,遍历map,并对map内部的Value只进行累加,返回结果。唯一的区别是参数Map的类型不一致,对开发者而言,虽然粘贴-复制的工作简单,能够快速的实现以上功能。但是其价值不高,而且也会在项目中造成很多冗余的代码,有没有一种方式,可以解决这种问题呢,答案就是使用泛型。

泛型函数

在本节中,我们将添加一个泛型函数,用来接收map类型的参数,不管Value值是 integer、float类型。从而使用一个函数替换之前的两个函数,让代码更加精简。

为了支持不同的类型进行累加计算,需要从两方面进行考虑

  • 函数定义 - 需要一种方法来声明函数支持的类型
  • 函数调用 - 需要一种方法来指定函数调用的类型(使用integer or float)

函数代码

// SumIntsOrFloats sums the values of map m. It supports both int64 and float64
// as types for map values.
func SumIntsOrFloats[K comparable, V int64 | float64](m map[K]V) V {
    var s V
    for _, v := range m {
        s += v
    }
    return s
}

comparable是golang新引入的预定义标识符,comparable仅能用于泛型中的类型限定,golang中对使用comparable限制key有如下约束

  • key类型必须定义比较操作符==和!=
  • key类型必须不能是function、map或slice
  • 对于interface类型,其动态类型必须定义比较操作符
  • 不满足上述约束,则会导致运行时异常(run-time panic)

函数调用一

fmt.Printf("Generic Sums: %v and %v\n",
    SumIntsOrFloats[string, int64](ints),
    SumIntsOrFloats[string, float64](floats))

执行代码

$ go run .

在这里插入图片描述

函数调用二

在上面调用泛型函数时,指定了Map Key-Value的参数类型,其实还有一种更简单的调用方式,无需指定Map数据类型,Golang编译器也能够从函数参数中自动的进行参数推断,调用方式如下

//在main.go main函数中新增代码
fmt.Printf("Generic Sums, type parameters inferred: %v and %v\n",
    SumIntsOrFloats(ints),
    SumIntsOrFloats(floats))

重新执行代码,结果如下

在这里插入图片描述

类型约束

开发者可以把前面定义的约束移动到它自己的接口中,以便可以在多个地方重用它。以这种方式声明约束有助于简化代码。将类型约束声明为接口。该约束允许实现接口的任何类型。例如,如果需要在三个函数上声明类型约束接口,然后将其与泛型函数中的类型参数一起使用,则用于调用该函数的类型参数必须适用于所有方法。

声明约束

type Number interface {
    int64 | float64
}

代码说明

  • 声明要用作类型约束的Number接口类型
  • 在接口内部声明数据类型,int64 、float64

泛型函数

func SumNumbers[K comparable, V Number](m map[K]V) V {
    var s V
    for _, v := range m {
        s += v
    }
    return s
}

接口调用

//在main.go main函数中新增代码
fmt.Printf("Generic Sums with Constraint: %v and %v\n",
    SumNumbers(ints),
    SumNumbers(floats))

执行代码

$ go run .

在这里插入图片描述

完整代码

package main

import "fmt"

type Number interface {
	int64 | float64
}

// SumInts adds together the values of m.
func SumInts(m map[string]int64) int64 {
	var s int64
	for _, v := range m {
		s += v
	}
	return s
}

// SumFloats adds together the values of m.
func SumFloats(m map[string]float64) float64 {
	var s float64
	for _, v := range m {
		s += v
	}
	return s
}

// as types for map values.
func SumIntsOrFloats[K comparable, V int64 | float64](m map[K]V) V {
	var s V
	for _, v := range m {
		s += v
	}
	return s
}

func SumNumbers[K comparable, V Number](m map[K]V) V {
	var s V
	for _, v := range m {
		s += v
	}
	return s
}

func main() {
	// Initialize a map for the integer values
	ints := map[string]int64{
		"first":  34,
		"second": 12,
	}

	// Initialize a map for the float values
	floats := map[string]float64{
		"first":  35.98,
		"second": 26.99,
	}

	fmt.Printf("Non-Generic Sums: %v and %v\n",
		SumInts(ints),
		SumFloats(floats))

	fmt.Printf("Generic Sums: %v and %v\n",
		SumIntsOrFloats[string, int64](ints),
		SumIntsOrFloats[string, float64](floats))

	fmt.Printf("Generic Sums, type parameters inferred: %v and %v\n",
		SumIntsOrFloats(ints),
		SumIntsOrFloats(floats))

	fmt.Printf("Generic Sums with Constraint: %v and %v\n",
		SumNumbers(ints),
		SumNumbers(floats))
}

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

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

相关文章

这些实体店直播必备技巧,新手直接套用就能火!

随着直播的受众越来越广、门槛越来越低,入局服装直播的实体店越来越多。对于服装厂商来说,服装产业链越靠下游毛利率越高,品牌商和销售商利润远高于加工生产商,约在40-50%,而服装制造商的毛利率仅在15%左右。而对于本土…

JDK8 新特性之收集Stream流中的结果

目录 一:Stream流中的结果到集合中 二:Stream流中的结果到数组中 三:对流中数据进行聚合计算 四:对流中数据进行分组 五:对流中数据进行多级分组 六:对流中数据进行分区 七:对流中数据进行拼接…

8.Java循环高级综合练习-无限循环和跳转控制语句,逢七过,平方根,判断是否为质数,猜数字小游戏

文章目录前言一、无限循环1.这三种循环中哪一种无限循环是最常用的呢?2.注意事项:二、跳转控制语句三、逢七过四、平方根五、判断该整数是否为一个质数六、猜数字小游戏保底机制总结前言 一、无限循环 1.这三种循环中哪一种无限循环是最常用的呢? 当然是右上角的while循环啦…

【若依】若依字典管理页面中列表按钮功能的实现

0. 功能实现描述 1. 代码实现 ScStfController.java /*** 查询员工证书* param stfId* param modelMap* return*/ RequiresPermissions("sc:stf:cert") GetMapping("/cert/{stfId}") public String detail(PathVariable("stfId") Long stfId, …

结构型模式-组合模式

1.概述 对于这个图片肯定会非常熟悉,上图我们可以看做是一个文件系统,对于这样的结构我们称之为树形结构。在树形结构中可以通过调用某个方法来遍历整个树,当我们找到某个叶子节点后,就可以对叶子节点进行相关的操作。可以将这颗树…

谷粒学院——Day19【项目部署】

❤ 作者主页:欢迎来到我的技术博客😎 ❀ 个人介绍:大家好,本人热衷于Java后端开发,欢迎来交流学习哦!( ̄▽ ̄)~* 🍊 如果文章对您有帮助,记得关注、点赞、收藏、…

Java中的hashCode,真的很容易弄懂

写这篇文章是因为在看hashMap源码时遇到有什么hashcode值,然后就去查,脑袋里面是有印象的,不就是在Object中有equals和hashcode方法嘛,这在学java基础的时候就遇到过,不过那时候无所谓,囫囵吞枣&#xff0c…

三、python基础语法进阶篇(黑马程序猿-python学习记录)

黑马程序猿的python学习视频:https://www.bilibili.com/video/BV1qW4y1a7fU/ 目录 一、文件操作 一、 文件的读取 1. 打开文件open() 2. 读取文件10个字节read(10) 3. 读取文件全部信息read() 4. 读取文件readLines() 5. 读取文件readLine() 6. for循环读取…

Nginx与LUA(7)

您好,我是湘王,这是我的CSDN博客。值此新春佳节,我给您拜年啦~祝您在新的一年中所求皆所愿,所行皆坦途,展宏“兔”,有钱“兔”,多喜乐,常安宁!软件开发中&…

使用小程序+网页简易实现多客户端实时弹幕

此文主要通过小程序网页模拟多客户端通过轮询、WebSockets、订阅推送等方式简易实现实时弹幕。 实现流程1、服务端1.1、创建项目2.2、接口定义2、客户端2.1、小程序端2.2、web端3、实现方式3.1、轮询3.2、WebSocket3.3、订阅推送实现流程 1、服务端 1.1、创建项目 打开Visual…

【docker概念和实践 5】容器命令和案例(1)

一、说明 docker的四个要素是:本地的Docker-engine、网上(本地)的仓库、镜像images、容器;初学者必须了解这是个概念的关系。但是,真正重要的概念是容器,因为,只有掌握了容器,才能具…

SpringBoot整合SSM

添加pom依赖 <dependency><groupId>org.projectlombok</groupId><artifactId>lombok</artifactId><version>1.16.18</version><scope>provided</scope></dependency><dependency><groupId>org.mybati…

macOS Monterey 12.6.3 (21G419) Boot ISO 原版可引导镜像

macOS Monterey 12.6&#xff0c;皆为安全更新&#xff0c;不再赘述。 macOS Monterey 12.6&#xff0c;发布于 2022 年 9 月 12 日&#xff08;北京时间今日凌晨&#xff09;&#xff0c;本次为安全更新。 今日&#xff08;2022-07-21&#xff09;凌晨&#xff0c;Apple 终于…

【Hadoop】HDFS高可用与高扩展原理分析(HA架构与Federation机制)

文章目录一、HDFS的高可用性&#xff08;HA架构&#xff09;二、HDFS的高扩展性&#xff08;Federation机制&#xff09;三、HA架构 Federation机制一、HDFS的高可用性&#xff08;HA架构&#xff09; 为保证HDFS的高可用性&#xff0c;即当NameNode节点机器出现故障而导致宕机…

【操作系统】—— Windows常用快捷键(带你快速了解)

&#x1f4dc; “作者 久绊A” 专注记录自己所整理的Java、web、sql等&#xff0c;IT技术干货、学习经验、面试资料、刷题记录&#xff0c;以及遇到的问题和解决方案&#xff0c;记录自己成长的点滴。 &#x1f341; 操作系统【带你快速了解】对于电脑来说&#xff0c;如果说…

【JavaEE】定时器的简单实现

目录 定时器 实现定时器 描述任务 保存任务 扫描任务 执行任务 定时器 在实现定时器之前&#xff0c;先来简单的了解一下什么是定时器。 定时器是软件开发中一个重要的组件。比如到了什么时候&#xff0c;干一件什么样的事情&#xff1b;多少秒之后干什么。本篇文章介绍…

活动星投票最美养生师展网络评选微信的投票方式线上免费投票

“最美养生师”网络评选投票_用户同什么方法挑选投票小程序_最好的投票小程序用户在使用微信投票的时候&#xff0c;需要功能齐全&#xff0c;又快捷方便的投票小程序。而“活动星投票”这款软件使用非常的方便&#xff0c;用户可以随时使用手机微信小程序获得线上投票服务&…

Hive函数大全–完整版(三)

官网参考地址&#xff1a; 官网UDF - Apache Hive 1. 基本数据类型 2. 基础运算符与函数 SQL结果A IS NULL 空A IS NOT NULL 非空 A LIKE B 模糊匹配A RLIKE B 正则表达式匹配A REGEXP B 正则表达式匹配 3. 类型转换 cast(expr as <type>)…

园区网典型组网架构及案例实践

什么是园区网园区网络是限定区域内&#xff0c;连接人与物的局域网络&#xff1b;园区网络通常只有一个管理主体&#xff1b;如果有多个管理主体&#xff0c;通常被认为为多个园区网络。园区网络典型架构小型园区网络典型架构小型园区网络应用于接入用户数量较少的场景&#xf…

SpringBoot 统一功能处理

SpringBoot 统一功能处理前言一、用户登录权限效验1.1 最初的用户登录验证1.2 Spring AOP 用户统一登录验证的问题1.3 Spring 拦截器1.3.1 准备工作1.3.2 自定义拦截器1.3.3 将自定义拦截器加入到系统配置1.4 拦截器实现原理1.4.1 实现原理源码分析1.4.2 拦截器小结1.5 扩展&am…