前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
圈层
工具
发布
首页
学习
活动
专区
圈层
工具
MCP广场
社区首页 >专栏 >深度思考:为什么需要泛型?

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

作者头像
闫同学
发布于 2022-10-31 07:03:17
发布于 2022-10-31 07:03:17
29900
代码可运行
举报
文章被收录于专栏:扯编程的淡扯编程的淡
运行总次数:0
代码可运行

不知道大家平时在进行后端编程的时候有没有考虑过一个概念:泛型编程,就像面向对象、面向接口编程一样,很常用以致于用成为了大家广泛的习惯,在后端常用编程语言中,无论是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泛型编程
代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
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泛型新特性》(http://t.csdn.cn/XUc5Z)

3 为什么需要泛型?

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

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
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);
    }
  }

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

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
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);
    }
 }

接下来我们引入泛型:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
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使用类似于具体化的方式实现泛型,就是在运行时使用类型信息,根据类型参数创建不同的具体类型的变量。

参考:

https://time.geekbang.org/column/article/485140

https://baike.baidu.com/item/%E6%B3%9B%E5%9E%8B/4475207?fr=aladdin

https://time.geekbang.org/column/article/283229

本文参与 腾讯云自媒体同步曝光计划,分享自微信公众号。
原始发表:2022-07-11,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 扯编程的淡 微信公众号,前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
暂无评论
推荐阅读
编辑精选文章
换一批
C#基础篇——泛型
在开发编程中,我们经常会遇到功能非常相似的功能模块,只是他们的处理的数据不一样,所以我们会分别采用多个方法来处理不同的数据类型。但是这个时候,我们就会想一个问题,有没有办法实现利用同一个方法来传递不同种类型的参数呢?
zls365
2021/04/23
1.4K0
C#基础篇——泛型
深入理解 Java 泛型
泛型要求在声明时指定实际数据类型,Java 编译器在编译时会对泛型代码做强类型检查,并在代码违反类型安全时发出告警。早发现,早治理,把隐患扼杀于摇篮,在编译时发现并修复错误所付出的代价远比在运行时小。
静默虚空
2022/03/23
4470
深入理解 Java 泛型
深入理解Golang的泛型
2022年3月15日,争议非常大但同时也备受期待的泛型终于伴随着Go1.18发布了。
KunkkaWu
2023/07/12
1.5K0
深入理解Golang的泛型
那些年我们在Java泛型上躺过的枪---万恶的泛型擦除【享学Java】
泛型(Generics),从字面的意思理解就是泛化的类型,即参数化类型。 我们都知道,泛型是JDK5提供的一个非常重要的新特性,它有非常多优秀的品质:能够把很多问题从运行期提前到编译器,从而使得程序更加的健壮。
YourBatman
2019/09/03
1K0
那些年我们在Java泛型上躺过的枪---万恶的泛型擦除【享学Java】
Go泛型和Java泛型有什么差距?
最近随着Go 1.18版本发布了,也就是Go正式支持generic编程了,这个版本的发布将会给你Go编程开发体验带来巨大变动,估计后面一些开源库也会陆续重构,对Go语言从发布至今应该是一次最大改动。
一大块芝士
2022/09/30
8060
Go泛型和Java泛型有什么差距?
transformation操作开发实战
1、map:将集合中每个元素乘以2 2、filter:过滤出集合中的偶数 3、flatMap:将行拆分为单词 4、groupByKey:将每个班级的成绩进行分组 5、reduceByKey:统计每个班级的总分 6、sortByKey:将学生分数进行排序 7、join:打印每个学生的成绩 8、cogroup:打印每个学生的成绩
编程那点事
2023/02/25
5220
Java 集合使用不当,Code Review 被 diss了!
有很多小伙伴在写代码的时候,有一些比较基础的问题没有考虑到,导致项目 Code Review 的时候被 diss。
Guide哥
2021/09/16
5150
编写高质量代码改善C#程序的157个建议[优先考虑泛型、避免在泛型中声明静态成员、为泛型参数设定约束]
  泛型并不是C#语言一开始就带有的特性,而是在FCL2.0之后实现的新功能。基于泛型,我们得以将类型参数化,以便更大范围地进行代码复用。同时,它减少了泛型类及泛型方法中的转型,确保了类型安全。委托本身是一种引用类型,它保存的也是托管堆中对象的引用,只不过这个引用比较特殊,它是对方法的引用。事件本身也是委托,它是委托组,C#中提供了关键字event来对事件进行特别区分。一旦我们开始编写稍微复杂的C#代码,就肯定离不开泛型、委托和事件。本章将针对这三个方面进行说明。
aehyok
2018/08/31
6520
编写高质量代码改善C#程序的157个建议[优先考虑泛型、避免在泛型中声明静态成员、为泛型参数设定约束]
花了几个小时总结了一些容易出错的 Java 知识点
Object的equals方法容易抛空指针异常,应使用常量或确定有值的对象来调用 equals。
Java_老男孩
2019/12/19
5620
Java & Go泛型对比
在当今软件开发领域中,泛型是一种强大的编程特性,它能够在不牺牲类型安全的前提下,实现代码的复用和灵活性。Java作为一种老牌的面向对象编程语言,在其长期的发展过程中,已经积累了丰富的泛型经验和应用场景。而Go语言作为一种相对较新的编程语言,也在不断探索和发展其泛型特性,以满足现代软件开发的需求。本文将对Java和Go语言的泛型进行比较和介绍,探讨它们的实现方式、语法特点以及适用场景,帮助读者更好地理解和应用泛型编程。
FunTester
2024/03/22
1900
Java & Go泛型对比
Arrays.asList()使用指南
List<String> list = Arrays.asList("a","b","c");
黑洞代码
2021/01/14
4670
Arrays.asList()使用指南
全面解读!Golang中泛型的使用
导语 | Golang在2022-03-15发布了V1.18正式版,里面包含了对泛型的支持,那么最新版本的泛型如何使用呢?有哪些坑呢?本文全面且详细的带你了解泛型在Golang中的使用。 一、什么是泛型 说起泛型这个词,可能有些人比较陌生,特别是PHP或者JavaScript这类弱语言的开发者,尤其陌生。因为在这些弱语言中,语法本身就是支持不同类型的变量调用的。可以说无形之中早已把泛型融入语言的DNA中了,以至于开发者习以为常了。 举个PHP中的泛型的例子: 我们定义了一个sum函数,参数是传入2个变量,返
腾讯云开发者
2022/06/24
8.7K2
全面解读!Golang中泛型的使用
Go语言中的泛型编程
在某些情况下,我们可能需要处理一些类型在编译时未知的数据。在这些情况下,可以将泛型和反射结合起来使用,既能享受泛型带来的类型安全,又能处理动态类型。
数字扫地僧
2024/07/05
1610
数组的三种声明方式总结、多维数组的遍历、Arrays类的常用方法总结
1. 数组的三种声明方式 public class WhatEver { public static void main(String[] args) { //第一种 例: String[] test1 = new String[6]; test1[0] = "数组0"; test1[1] = "数组1"; //第二种 例: String[] test2 = {"数组0","数组1","数组2",
泰斗贤若如
2019/06/19
7600
六、泛型【黑马JavaSE笔记】
泛型:是JDK5中引入的特性,它提供了编译时类型安全检测机制,该机制允许在编译时检测到非法的类型
啵啵鱼
2022/11/23
3510
Java---数组
数组是一组具有连接性,相同类型的存储空间。数据结构(C)中被称之为线性表(顺序存储)。数组的长度固定不可变化。
用户10787181
2023/10/17
2310
Java---数组
Arrays.asList()使用指南
最近使用Arrays.asList()遇到了一些坑,然后在网上看到这篇文章:http://javadevnotes.com/java-array-to-list-examples 感觉挺不错的,但是还不是很全面而且是英文的。所以,自己对于这块小知识点进行了简单的总结
乔戈里
2019/07/19
4260
Java泛型详解:为什么使用泛型?如何使用泛型?
大家好!今天我要和大家一起探讨的是Java泛型,一个让我们的代码更加灵活、可读性更强的强大特性。相信很多人都听说过泛型,但对于为什么使用泛型、如何使用泛型以及泛型的实现原理和本质,可能还有些困惑。别担心,我会通过通俗易懂的语言,带你深入了解这一话题,并为你提供一些实例演示。
默 语
2024/11/20
4220
Go 泛型浅析
泛型程序设计(generic programming)是程序设计语言的一种风格或范式。泛型允许程序员在强类型程序设计语言中编写代码时使用一些以后才指定的类型,在实例化时作为参数指明这些类型。
mousemin
2023/06/10
5720
Go 泛型浅析
游刃有余:玩转Java泛型
Java 中的泛型提供了一种创建可以处理不同类型数据的可重用代码的方法。它允许用户定义可操作各种数据类型的类、接口和方法,而无需牺牲类型安全性。在 Java 5 中引入的泛型已经成为 Java 编程语言的一个基本特性。
FunTester
2023/12/19
2140
游刃有余:玩转Java泛型
推荐阅读
相关推荐
C#基础篇——泛型
更多 >
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档
本文部分代码块支持一键运行,欢迎体验
本文部分代码块支持一键运行,欢迎体验