深度思考:为什么需要泛型?

简介: 深度思考:为什么需要泛型?

不知道大家平时在进行后端编程的时候有没有考虑过一个概念:泛型编程,就像面向对象、面向接口编程一样,很常用以致于用成为了大家广泛的习惯,在后端常用编程语言中,无论是Java、C++都支持泛型编程,而在2022年的3月份,随着Go1.18稳定版本的发布,Go自1.18版本起,也支持了“泛型(Generics)”这一特性,这同时也是Go语言在语法层面的一次重大改变。

虽然之前在使用Java进行编程时经常用到泛型,但是未曾思考过到底为什么需要泛型?没有泛型会怎样?泛型带来了什么作用?泛型的实现原理是怎样的?等等问题。

因为Go1.18版本发布已有几个月的时间,各个IDE也陆续支持Go语言泛型编码,因此也通过一些资料学习了Go语言泛型这个新特性,并且对此做了一些思考,想以一篇文章来向大家分享自己的思考经验和见解,同时也会以实际代码的方式使用Java、Go语言的泛型特性,剖析其原理,下面开始正文。

1 什么是泛型?

维基百科提到:最初泛型编程这个概念来自于缪斯·大卫和斯捷潘诺夫. 亚历山大合著的“泛型编程”一文。那篇文章对泛型编程的诠释是:“泛型编程的中心思想是对具体的、高效的算法进行抽象,以获得通用的算法,然后这些算法可以与不同的数据表示法结合起来,产生各种各样有用的软件”。说白了就是将算法与类型解耦,实现算法更广泛的复用。

在我看来,泛型是同接口类似,也是编程的一种规范,也可以说是一种风格,泛型编程可以让开发者在编写代码时约束变量、容器、对象、结构体的类型,对类型清晰的掌握可以减少bug的产生,增强代码的可读性,让抽象变得更加具体和实用。基于泛型的程序,由于传入的参数不同,程序会实现不同的功能。这也被叫做一种多态现象,叫做参数化多态(Parametric Polymorphism)。

2 编程语言中泛型编程的实例

2.1 Java泛型编程

请移步这篇文章《玩转Java泛型》

2.2 Go泛型编程

package main
import "fmt"
type MyList[T any] struct {
   Items []Item[T]
}
type Item[T any] struct {
   Index int
   Value T
}
func (list *MyList[T]) AddItem(i T) {
   item := Item[T]{Value: i, Index: len(list.Items)}
   list.Items = append(list.Items, item)
}
func (list *MyList[T]) GetItem(index int) T {
   l := list.Items
   var val T
   for i := range l {
      if l[i].Index == index {
         val = l[i].Value
      }
   }
   return val
}
func (list *MyList[T]) Print() {
   for i := range list.Items {
      fmt.Println(list.Items[i])
   }
}
type MyHashMap[K comparable, V any] struct {
   Value map[K]V
}
func (m *MyHashMap[K, V]) SetValue(k K, v V) {
   m.Value[k] = v
}
func (m *MyHashMap[K, V]) GetValue(k K) V {
   return m.Value[k]
}
func (m *MyHashMap[K, V]) Print() {
   for k := range m.Value {
      fmt.Println(k, m.Value[k])
   }
}
func main() {
   list := MyList[int]{}
   list.AddItem(1)
   list.AddItem(2)
   item := list.GetItem(7)
   list.Print()
   hashMap := MyHashMap[string, int]{map[string]int{"A": 1, "B": 2}}
   hashMap.SetValue("s", 2)
   value := hashMap.GetValue("s")
   hashMap.Print()
}
复制代码

具体Go泛型编程内容可以看下这篇文章哈:《一文搞懂Go1.18泛型新特性》

3 为什么需要泛型?

回答这个问题之前,我们不妨思考下,在一些场景下如果没有泛型会怎样:

public class Main {
    static class Score {
        String name;
        int num;
        public Score(String name, int num) {
            this.name = name;
            this.num = num;
        }
    }
    public static int getSum(List<Score> scores) {
        int sum = 0;
        for (Score score : scores) {
            sum += score.num;
        }
        return sum;
    }
    public static void main(String[] args) {
        List<Score> scores = Arrays.asList(
                new Score("zs", 100),
                new Score("ls", 80),
                new Score("ww", 90.5) //编译不通过
        );
        int sum = getSum(scores);
        System.out.println(sum);
    }
}
复制代码

没有泛型时解决上述问题:

public class Main {
    static class Score {
        String name;
        int num;
        public Score(String name, int num) {
            this.name = name;
            this.num = num;
        }
    }
    static class Score2 {
        String name;
        float num;
        public Score2(String name, float num) {
            this.name = name;
            this.num = num;
        }
    }
    public static int getIntSum(List<Score> scores) {
        int sum = 0;
        for (Score score : scores) {
            sum += score.num;
        }
        return sum;
    }
    public static float getFloatSum(List<Score2> scores) {
        float sum = 0;
        for (Score2 score : scores) {
            sum += score.num;
        }
        return sum;
    }
    public static void main(String[] args) {
        List<Score> scores = Arrays.asList(
                new Score("zs", 100),
                new Score("ls", 80)
        );
        List<Score2> scores2 = Arrays.asList(
                new Score2("zs", 89.5f),
                new Score2("ls", 80.5f)
        );
        int sum = getIntSum(scores);
        float sum2 = getFloatSum(scores2);
        System.out.println(sum+sum2);
    }
}
复制代码

接下来我们引入泛型:

public class Main {
    static class Score<T> {
        String name;
        T num;
        public Score(String name, T num) {
            this.name = name;
            this.num = num;
        }
    }
    public static int getIntSum(List<Score<Integer>> scores) {
        int sum = 0;
        for (Score<Integer> score : scores) {
            sum += score.num;
        }
        return sum;
    }
    public static float getFloatSum(List<Score<Float>> scores) {
        float sum = 0;
        for (Score<Float> score : scores) {
            sum += score.num;
        }
        return sum;
    }
    public static void main(String[] args) {
        List<Score<Integer>> scores = Arrays.asList(
                new Score("zs", 100),
                new Score("ls", 80)
        );
        List<Score<Float>> scores2 = Arrays.asList(
                new Score("zs", 89.5f),
                new Score("ls", 80.5f)
        );
        int sum = getIntSum(scores);
        float sum2 = getFloatSum(scores2);
        System.out.println(sum+sum2);
    }
}
复制代码

所以,使用泛型的原因:

  • 泛化
  • 类型安全
  • 消除强制类型转换
  • 向后兼容

图示:

网络异常,图片无法展示
|


4 总结泛型的实现原理

大多数静态类型语言的泛型实现都是在编译期进行,也就是编译的前端实现,主要的技术包括类型擦除、具体化和基于元编程等进行的,比如Java的泛型就是基于类型擦除实现,在编译前端进行类型检查即可,编译之后的字节码不管有没有泛型都是一样的,运行时也是如此。而Go语言的泛型实现则不同,Go使用类似于具体化的方式实现泛型,就是在运行时使用类型信息,根据类型参数创建不同的具体类型的变量。

参考:

time.geekbang.org/column/arti…

baike.baidu.com/item/%E6%B3…

time.geekbang.org/column/arti…


相关文章
|
存储
Typora上传图片后提示 “image load failed“ 无法加载出图片
Typora上传图片后提示 “image load failed“ 无法加载出图片
3499 1
Typora上传图片后提示 “image load failed“ 无法加载出图片
|
人工智能
【AI绘画】ControlNet 之 Reference only 锁定面部跑图
【AI绘画】ControlNet 之 Reference only 锁定面部跑图
1909 0
|
算法 编译器 数据处理
什么叫下推
`下推`在计算机科学中涉及多种应用:1) 在编译器的下推自动机用于语法分析;2) 栈操作在函数调用与递归中管理返回地址和局部变量;3) 索引下推优化数据库查询性能;4) 事件下推在异步编程和事件驱动架构中处理事件;5) 下推算法在流处理中实现接近数据源的计算,提升流式数据处理效率。
823 2
|
9月前
|
人工智能 自然语言处理 供应链
深度揭秘 RPA:从录屏脚本到智能员工,它的进化之路有多惊艳?
RPA机器人是能自动处理重复性工作的“数字员工”,通过模拟人工操作,实现数据录入、报表填报等任务的自动化。它非实体、不需编程,广泛应用于金融、制造、医疗等领域,提升效率、降低错误。未来,AI赋能的RPA将更智能、普惠,推动人机协同新变革。(239字)
1469 1
|
数据采集 监控 前端开发
建议用API来获取电商的商品数据,但还是需要用爬虫
在电商数据获取中,“优先用 API、辅以爬虫”是务实策略。API 满足合规稳定的核心需求,而爬虫则在权限限制、数据不全、成本过高或跨平台整合时发挥关键补充作用。本文从 API 局限性、爬虫不可替代场景及协同方案三方面,详解如何高效结合两者,实现合规又全面的数据采集与应用。
|
8月前
|
人工智能 算法 API
AI英语口语APP的开发
本项目开发AI英语口语APP,深度融合ASR、LLM、TTS与音素级口语评测。优选通义千问、GLM-4等国产大模型,支持低延迟流式交互、多口音TTS及场景化分级训练,专注真实口语提升。(239字)
|
小程序 搜索推荐 JavaScript
微信小程序使用Vant
微信小程序使用Vant
904 2
|
文件存储 数据安全/隐私保护 开发者
群晖NAS Docker镜像源加速配置教程
本教程介绍了群晖NAS用户如何通过配置轩辕镜像加速服务提升Docker镜像拉取速度。内容包括配置前准备、详细设置步骤及日常使用说明,帮助用户快速完成配置并享受高效稳定的镜像下载体验。
7184 60
|
搜索推荐 Linux iOS开发
qBittorrent:专业级磁力种子下载工具,高速稳定 + 全功能资源管理
qBittorrent 是一款免费、开源且无广告的 P2P BitTorrent 客户端,支持 Windows、Mac 和 Linux 系统。它功能强大,包含 DHT、Peer Exchange、加密等技术,支持下载优先级设置、RSS 订阅和远程控制。用户可通过 Torrent 文件或磁力链接下载资源,并能优化连接设置以提升速度。常见问题如“元数据下载”或 DHT 连接不佳时,建议使用热门种子或调整 trackers。
14142 0
|
安全 搜索推荐 机器学习/深度学习
AI赋能教育:深度学习在个性化学习系统中的应用
【10月更文挑战第26天】在人工智能的推动下,个性化学习系统逐渐成为教育领域的重要趋势。深度学习作为AI的核心技术,在构建个性化学习系统中发挥关键作用。本文探讨了深度学习在个性化推荐系统、智能辅导系统和学习行为分析中的应用,并提供了代码示例,展示了如何使用Keras构建模型预测学生对课程的兴趣。尽管面临数据隐私和模型可解释性等挑战,深度学习仍有望为教育带来更个性化和高效的学习体验。
859 0

热门文章

最新文章