广告
返回顶部
首页 > 资讯 > 后端开发 > GO >浅谈Go1.18中的泛型编程
  • 782
分享到

浅谈Go1.18中的泛型编程

GO泛型泛型编程 2022-06-07 20:06:41 782人浏览 安东尼
摘要

目录前言以前的Go泛型泛型是什么Go的泛型泛型函数泛型类型类型集合和接口的差异总结前言 经过这几年的千呼万唤,简洁的Go语言终于在1.18版本迎来泛型编程。作为一门已经有了1

目录

前言

以前的Go泛型

泛型是什么

Go的泛型

泛型函数

泛型类型

类型集合

和接口的差异

总结

前言

经过这几年的千呼万唤,简洁的Go语言终于在1.18版本迎来泛型编程。作为一门已经有了14年历史的强类型语言,很难相信它到现在才开始有一个正式的泛型。

以前的Go泛型

虽然直到1.18版本才加入泛型,但是在2014年便有相关的讨论要在Go中加入泛型设计。但是由于各种原因没有实现。而之后的接口(interface)的提出,让泛型进一步搁置。但是由于接口的缺陷,最终Go团队还是在1.18的版本中加入了泛型。实际上,这一版本的泛型设计在语言层面和接口非常相似(在实现层面肯定是不一样的,泛型是编译时,接口是运行时),对于他们之间的差异,也会在后面提到。

本文主要讲述1.18beta1版本中的泛型,后续有改动,可能会更改文章。

泛型是什么

在我看来泛型其实用c++的模板一词来描述就非常的准确。在写代码的时候,我们经常需要写很多重复的逻辑,一般这个时候我们就会使用函数来对其进行封装。但是由于Go是一种强类型语言,所以在定义和书写函数的时候需要在调用前标明类型。当然如果这一重复的逻辑只需要固定的类型,这样就足够了,但是很多时候我们需要不同的类型进行类似的逻辑,譬如我们刚刚看到的GIF。对于普通开发人员来说这种情况可能遇到的比较少,但是在一些库开发人员来说,这种情况变得非常的普遍。

泛型程序设计(generic programming)是程序设计语言的一种风格或范式。泛型允许程序员在强类型程序设计语言中编写代码时使用一些以后才指定的类型,在实例化时作为参数指明这些类型。各种程序设计语言和其编译器、运行环境对泛型的支持均不一样。Ada、Delphi、Eiffel、Java、C#、F#、Swift 和 Visual Basic .net 称之为泛型(generics);ML、Scala 和 Haskell 称之为参数多态(parametric polymorphism);C++ 和 D称之为模板。具有广泛影响的1994年版的《Design Patterns》一书称之为参数化类型(parameterized type)。

其中,C++的模版应该是做的最完善的,不仅支持简单的模板替换,还可以处理一些简单的逻辑,经过不断的迭代,已经形成了一种生成代码的编程方式,因此也叫做模板元编程(Template metaprogramming)。当然由于其和C++编程方式完全不一致,所以可读性非常的差。而在Go的泛型设计中,为了保证泛型的简洁,Go并不支持模版元编程(心塞,还想试试在Go里面往往骚操作呢)。

Go的泛型

接下来就是Go泛型的使用介绍了,Go支持泛型函数和泛型类型。

泛型函数

先来一个最简单的泛型函数


func ink19FirstGen[T any](t T) {
 fmt.Println(t)
}

这是一个非常简单的的函数,就是使用fmt.Println打印输入的参数。相比于以前的函数,多了[T any]部分,这就是Go泛型的参数列表。

参数列表中的参数由两部分组成,参数名和约束,其中T就是参数,any为参数的约束。从表达上来说,和Go语言一贯的风格相似,名在前,类型在后。

在Go语言中,使用接口interface做为类型的约束,其中any = interface{},即为无限制,但是以其说是无限制,倒不如说是完全限制,由于any里面没有定义任何的方法,所以在函数里面也没办法调用t的任何方法。

这里有一个非常重要的问题,就是相比较于C++的模板,Go会在定义函数的时候就对函数进行解析。所以在函数中使用了的方法,一定要在约束的接口中出现。


type ink19Inf interface {
 Test()
}
func ink19FirstGen[T ink19Inf](t T) {
 t.Test()
}

和普通参数类似的,如果是相同的约束,参数类型也支持简化


func ink19FirstGen[T ,T2 ink19Inf](t T, t2 []T2) {
 t.Test()
}
泛型类型

和C++中的模板类类似的,Go里面也有泛型类型,它的定义也很简单


type ink19Vector[T any] []T

结构相比与以前的类型定义多了[T any]部分,这一部分的结构和泛型函数那一部分类似就不多介绍了。

对于泛型类型,Go也可以定义相关的方法,譬如:


func (m *ink19Vector[T]) Push(v T) *ink19Vector[T] {
 *m = append(*m, v)
 return m
}

在泛型结构体中,结构体也可以定义自己的类型的变量,形成链表


type List[T1, T2 any] struct {
 next *List[T1, T2]
 t1 T1
 t2 T2
}

PS:依据提案中的说法,第二行的参数列表应该和定义中的顺序一致,以防止无限递归。但是在1.18beta1版本的实测中,顺序不一致的写法并不会报错。

Go暂时不支持方法的泛型。

类型集合

虽然通过接口限制类型可以满足绝大部分的要求,但是仍然有一些需求满足不了,譬如运算符。假如我们有一个函数,可以传入任意可比较的参数,然后返回较小的那一个。很自然的,我们可以写下如下的代码:


func whoismin[T any](a, b T) T {
  if a < b {
    return a
  }
  return b
}

但是,很遗憾的,由于我们对T的约束是any。所以其实来说,我们没办法对a和b做任何的操作,对比也是。所以在这里,我们会收到报错


invalid operation: cannot compare a < b (operator < not defined on T)

为了解决这一问题,提案中提出了类型集合的概念。

对于一个类型,认为它代表的类型集合就是只包含这个类型的集合,即对于类型M来说,其代表的类型集合为{M}。而对于接口来说,其对应的类型集合是无限的,只要一个类型满足接口的所有方法签名,那么这个类型就是属于这个接口的类型集合中。其实很容易理解类型集合就是那个识别符可以代表的类型的集合。

考虑集合的操作,对于下面这个例子


type ink19Inf1 interface {
 What1()
}
type ink19Inf2 interface {
 What2()
}
type ink19Inf3 interface {
 What1()
 What2()
}

假设ink19Inf1的类型集合为A,ink19Inf2的为B,ink19Inf3的为C。那么很容得到C=A⋂B。即C为A和B的交集。当然只有交集是不行的,后面还有说明实现并集。

为了进一步的说明类型集合,我们先来回忆一下接口的定义,对于之前的接口来说,接口的元素一共有两种:方法签名和其他接口。


type ink19Inf1 interface {
 What()
}
type ink19Inf2 interface {
 ink19Inf1
 It()
}

比如ink19Inf2中的第一个元素就是其他接口,第二个元素是其他签名。但是仅仅只是有这两种元素,对于泛型约束来说是完全不够的。为此,提案中加入了另外三种不同的元素,需要注意的是,如果一个接口加入了这额外三种元素,那么这个接口就不能再作为普通的接口使用了,只能用作泛型。

第一个增加的是类型元素。以前的接口是不能用类型作为接口的,但是在作为约束中可以这样操作。作为元素的时候就是提供了一个只包含自己本身的类型作为元素的类型集合。

第二个是增加了近似约束元素,写法是在类型前面增加~符号,如


type ink19Inf1 interface {
 ~int
}

这一个元素的意义是为接口提供了一个所有以int为底层类型的集合。所以被~修饰的类型也应该是一个底层类型,不然提供的集合就是空集,没有任何意义。具体的区别可以看下面的这个例子。


type ink19Inf3 interface {
 int
}
type ink19Inf4 interface {
 ~int
}
type MyInt int

首先我们定义了两个接口,第一个接口使用的是额外的第一种元素, 因此它的类型集合只包含了int。另一个使用了第二种元素,它的类型集合包含了所有以int为底层类型的类型。然后我们定义了一个MyInt类型,它是以int为底层类型的类型。需要注意的是,在Go中MyInt和int是两种不同的类型。最后我们写两个方法来分别使用两个接口为约束。


func ink19Print1[T ink19Inf3](t T) {
 fmt.Println(t)
}
func ink19Print2[T ink19Inf4](t T) {
 fmt.Println(t)
}
var data MyInt = 1
ink19Print1(data)  // 错误
ink19Print2(data)

第三个元素是联合约束。使用方法如下


type ink19Inf5 interface {
 int | float32 | bool | ~string | ink19Inf3
}

使用方法非常简单,就是将并集的元素一个一个使用|连接就就好了。需要注意的是联合约束的元素只支持类型,近视约束和其他只包含以上三种额外元素的接口(即,不支持包含方法签名的接口)。

回到之前的问题,对于需要使用操作符的情况,有了以上的工具后就可以解决了。

纵观整个Go语言,由于并不支持操作符,所以有操作符(除了==和!=)的其实只有有限的几种类型,譬如:int,float32,string等等。

所以对于需要使用比较运算符的约束的时候,可以使用如下的一个约束接口:


type Ordered interface {
 ~int | ~int8 | ~int16 | ~int32 | ~int64 |
  ~uint | ~uint8 | ~uint16 | ~uint32 | ~uint64 | ~uintptr |
  ~float32 | ~float64 |
  ~string
}

为了方便使用,Go标准库里面提供了一个constraints来提供相关的约束。

上面提到,对于除了==和!=以外的操作符可以通过对所有的类型进行枚举来实现。但是对于这两个操作符,用户自定义的类型也会有这两个操作符,没办法枚举实现。官方给出的方法是通过使用一个一个内建的约束comparable来完成操作。譬如


func IsSame[T comparable](a T, b T) bool {
 return a == b
}
和接口的差异

由于本人对于Go的接口使用并不多,所以如果有不足的地方请及时指正。

实现方法上,泛型是编译时,接口是运行时;

可以实现操作符的约束;

返回的参数可以是特定的类型,而接口只能返回固定的接口类型;

相比较于接口,泛型的约束可以有更多的操作。

总结

以上就是Go语言泛型的使用。总的来说,比较完整,实现了大部分的功能,相比于接口,有一定的差异。从体验上来说有较高的提升,但是其缺点也非常的多。首先是其后面提出的三种元素,它将接口和类型限制隔离开了,这是一个特别奇葩的操作,感觉不符合Go语言的简洁实现。添加的三种元素中,我们主要来看第三种,联合。代码在分析的时候会对每一个元素测试,看看能不能通过编译。所以从集合的角度上来看,如果我们把一个类型可以进行的操作可做是一个集合,那么这一个联合就是在一个限定的类型集合里面(枚举出的)对每一个类型的操作集合进行一个交集操作。回到原来,其实出现这个语法特性的最大原因就是Go语言不支持操作符重载,所以没办法对操作符进行枚举,那为什么不直接在这个版本实现操作符重载呢?或者直接不考虑这一部分,让传入的结构体只能使用方法,不能使用操作符。并且,即使加入了这三种元素,还是有两种操作符!=和==无法使用现在有的实现,只能使用一个内建的符号来代表这一类的方法,个人感觉非常丑陋。

到此这篇关于浅谈Go1.18中的泛型编程的文章就介绍到这了,更多相关Go  泛型编程内容请搜索编程网以前的文章或继续浏览下面的相关文章希望大家以后多多支持编程网!


您可能感兴趣的文档:

--结束END--

本文标题: 浅谈Go1.18中的泛型编程

本文链接: https://www.lsjlt.com/news/30732.html(转载时请注明来源链接)

有问题或投稿请发送至: 邮箱/279061341@qq.com    QQ/279061341

本篇文章演示代码以及资料文档资料下载

下载Word文档到电脑,方便收藏和打印~

下载Word文档
猜你喜欢
  • 浅谈Go1.18中的泛型编程
    目录前言以前的Go泛型泛型是什么Go的泛型泛型函数泛型类型类型集合和接口的差异总结前言 经过这几年的千呼万唤,简洁的Go语言终于在1.18版本迎来泛型编程。作为一门已经有了1...
    99+
    2022-06-07
    GO 泛型 泛型编程
  • Go1.18中泛型编程的示例分析
    小编给大家分享一下Go1.18中泛型编程的示例分析,相信大部分人都还不怎么了解,因此分享这篇文章给大家参考一下,希望大家阅读完这篇文章后大有收获,下面让我们一起去了解一下吧!前言经过这几年的千呼万唤,简洁的Go语言终于在1.18版本迎来泛型...
    99+
    2023-06-22
  • 浅谈C#泛型的用处与特点
    泛型是 2.0 版 C# 语言和公共语言运行库 (CLR) 中的一个新功能。泛型将类型参数的概念引入 .NET Framework,类型参数使得设计如下类和方法成为可能:这些类和方法...
    99+
    2022-11-15
    C#泛型
  • 浅谈Python的元编程
    目录一、装饰器二、装饰器的执行顺序三、元类四、descriptor 类(描述符类)五、总结 相应的元编程就是描述代码本身的代码,元编程就是关于创建操作源代码(比如修改、生成或包装原来...
    99+
    2022-11-12
  • 怎么浅谈Java并发编程中的Java内存模型
    这篇文章的内容主要围绕怎么浅谈Java并发编程中的Java内存模型进行讲述,文章内容清晰易懂,条理清晰,非常适合新手学习,值得大家去阅读。感兴趣的朋友可以跟随小编一起阅读吧。希望大家通过这篇文章有所收获!物理计算机并发问题在介绍Java内存...
    99+
    2023-06-17
  • 浅谈Java中的桥接方法与泛型的逆变和协变
    目录1. 泛型的协变1.1 泛型协变的使用1.2 泛型协变存在的问题1.2.1 Java当中桥接方法的来由1.2.2 为什么泛型协变时,不允许添加元素呢1.2.3 从Java字节码的...
    99+
    2022-11-13
  • 浅谈node.js中async异步编程
    1.什么是异步编程? 异步编程是指由于异步I/O等因素,无法同步获得执行结果时, 在回调函数中进行下一步操作的代码编写风格,常见的如setTimeout函数、ajax请求等等。 示例: for (v...
    99+
    2022-06-04
    浅谈 node async
  • 浅谈Java编程中的synthetic关键字
    java synthetic关键字。有synthetic标记的field和method是class内部使用的,正常的源代码里不会出现synthetic field。小颖编译工具用的就是jad.所有反编译工具都不能保证完全正确地反编译clas...
    99+
    2023-05-31
    java synthetic 关键字
  • 浅谈Node异步编程的机制
    本文介绍了Node异步编程,分享给大家,具体如下: 目前的异步编程主要解决方案有: 事件发布/订阅模式 Promise/Deferred模式 流程控制库 事件发布/订阅模式 Node自身提供...
    99+
    2022-06-04
    浅谈 机制 Node
  • 浅谈Swoole并发编程的魅力
    目录场景介绍并发编程编码实现并发难题数据同步问题思维转变场景介绍 假设我们要做一个石头剪刀布的Web游戏,3个玩家同时提交竞猜后显示胜者。在传统串行化Web编程中,我们一般思路是这样...
    99+
    2022-11-12
  • 浅谈Java编程中string的理解与运用
    一,“==”与equals()运行以下代码,如何解释其输出结果?public class StringPool { public static void main(String args[]) { String s0="Hello...
    99+
    2023-05-30
    java string ava
  • 浅谈Java、PHP、C++编程的优缺点
    Java 、PHP、C++ 编程语言都是非常流行的编程语言,在开发、Web 开发、移动应用开发等领域都有广泛的应用。本文将从以下几个方面分析 Java、PHP、C++ 编程语言的优缺点。   一、Java 编程语言的优缺点  优点 (1)...
    99+
    2023-08-31
    php java c++
  • Java泛型中<?>和<T>的区别浅析
    目录一、定义1、T 代表一种类型2、是通配符,泛指所有类型二、使用1、T 一般有两种用途2、<> 的限制用途3、三种泛型限定三、总结1、从定义上看2、从用途上看补充:场景...
    99+
    2022-12-19
    java泛型?和T的区别 java 泛型 T ? Java 泛型 ?
  • 浅谈Java编程ToString()方法重写的意义
    上一篇文章我们介绍了java tostring方法重写代码示例,接下来,我们简单聊聊java编程tostring()方法重写的意义。toString()就是是重写,对于一般的对象来说都会有这个方法,其实这个方法的目的,主要就是将对象按字符串...
    99+
    2023-05-31
    java tostring() ava
  • 泛型和元编程的模型是什么
    这篇文章主要介绍“泛型和元编程的模型是什么”,在日常操作中,相信很多人在泛型和元编程的模型是什么问题上存在疑惑,小编查阅了各式资料,整理出简单好用的操作方法,希望对大家解答”泛型和元编程的模型是什么”的疑惑有所帮助!接下来,请跟着小编一起来...
    99+
    2023-06-15
  • 浅谈Python 中整型对象的存储问题
    在 Python 整型对象所存储的位置是不同的, 有一些是一直存储在某个存储里面, 而其它的, 则在使用时开辟出空间. 说这句话的理由, 可以看看如下代码: a = 5 b = 5 a is b # T...
    99+
    2022-06-04
    浅谈 整型 对象
  • 深入浅析java中集合泛型的本质
    深入浅析java中集合泛型的本质?相信很多没有经验的人对此束手无策,为此本文总结了问题出现的原因和解决方法,通过这篇文章希望你能解决这个问题。1、初始化两个集合,一个使用泛型,一个不使用ArrayList list1 = new Array...
    99+
    2023-05-31
    java 集合泛型 ava
  • C++泛型编程Generic Programming的使用
    目录一、容器 array vector deque list map 键值对key/value 二、迭代器iterator(泛型指针) 三、泛型算法Generic Programmi...
    99+
    2022-11-12
  • 浅谈python中的变量默认是什么类型
    1、type(变量名),输出的结果就是变量的类型; 例如 >>> type(6) <type 'int'> 2、在Python里面变量在声明时,不需要指定变量的类型,变量的类型...
    99+
    2022-06-04
    浅谈 变量 类型
  • C语言中如何实现泛型编程
    今天小编给大家分享一下C语言中如何实现泛型编程的相关知识点,内容详细,逻辑清晰,相信大部分人都还太了解这方面的知识,所以分享这篇文章给大家参考一下,希望大家阅读完这篇文章后有所收获,下面我们一起来了解一下吧。泛型编程(generic &nb...
    99+
    2023-06-17
软考高级职称资格查询
编程网,编程工程师的家园,是目前国内优秀的开源技术社区之一,形成了由开源软件库、代码分享、资讯、协作翻译、讨论区和博客等几大频道内容,为IT开发者提供了一个发现、使用、并交流开源技术的平台。
  • 官方手机版

  • 微信公众号

  • 商务合作