条条大路通罗马,各语言的泛型实现比较:CPP JAVA GO

条条大路通罗马,各语言的泛型实现比较:CPP JAVA GO

迄今为止,如果给所有阅读过的技术类书籍排序,CSAPP在我心目中稳居前五,因为在它的视角里,复杂丰繁的技术表象里,内在的原理逻辑反而是简化统一的,这样简化统一以后,计算机技术的发展脉络就异常清晰。
语言的实现,也是类似,C++JavaGo是目前使用的比较多的高级语言,对于同一个问题,他们各自会有什么样的处理方案,又有哪些共性和差异,这是非常有意思的视角,所以在这篇文章里,我想横向对比一下,也有助于在更广阔的视野里深化对语言机制的理解。知其然不如知其所以然。
泛型是广泛存在于各个语言中的概念,学习一门语言,我们初期就会花相当长的时间在容器概念上。
容器是泛型使用最频繁的地方,可以有效的节省代码量,目前高级语言实现泛型的方式,主要有两种:
这件事,总归要做,要么编译期做,要么运行时做,看你的关注点了。
除了上面两种,对于弱类型语言来说,我连类型都没有,泛型本就是天生支持!赢麻了。
这里不得不提C语言,C++和C语言究竟算是弱类型还是强类型语言,这个话题深究的话其实是有争议的,你说他是弱类型语言,他们是有类型声明的,你说是强类型语言,有Void*这种东西,再配合强制类型转换一起用,可不就是弱类型了么,可以变成你想要的各种模样。当然,现代C++已经不太提倡这种方式了。
有时候我们讨厌弱类型,或者动态类型语言,嫌弃他们太过灵活,不便操控,但是用久了强类型的语言,我们又特别变扭,硬是在C++里造出类型推导的auto、万能类型的any,在java里使用var。
足见类型这个东西,很是拧巴。
CPP的泛型实现是比较中古的方式,对新手不太友好的模板元编程。尽管它的名声不好,但是后来所谓的参数类型、类型参数,我觉得都可以看做是对模板元编程的模仿和改进。
因为最朴素,所以上手不难,但是花样太多,很难精通。嗯,很符合C++的整体气质。
上一个简单的例子:
C++的泛型就是这么简单,使用起来就是:
有什么忌讳和注意点么?
几乎没有,和使用一个普通的类几乎没有区别,理解这段程序,你只需要将T替换为实际传入的类型实参即可。
C++的实现也很简单粗暴,在编译的时候,检测源文件中传入的类型,针对每种类型编译一套结果。
但是,C++的模板编程不止于此,我们可以给类型形参设置默认类型、模板的形参也可以不是类型,而是一个固定常量,严格来说这些和泛型编程已经关系不大了,演化出了一种独特的编程哲学:模板元编程。核心就是利用这些规则和手段,将运行期的性能消耗转化到编译期。这里太过复杂,就不展开了。
在java虚拟机里,其实并不存在泛型这种概念,所有的泛型对象,都是普通对象。关键词:类型擦除
当我们定义一个泛型类型,编译后会成为一个原始的基本类型,就是删除了类型参数的类型名。
举个例子,我们自己编写一个泛型的容器类MyPair:
使用起来就像:
最终编译结果里可以看到,就只有一个class文件:
通过类型擦除,在JVM中,MyPair类型,实际是这样的:
java里默认所有类型会继承自Object,所以这样操作也是不会有什么问题的。与C++不同,即使是在使用的过程中,会有不同的类型参数,也不会产生新的定义。
原始类型用第一个限定的类型变量来替换,如果没有给定限定就用Object替换,对于一些比较复杂的泛型类声明,比如:
这里Interval类使用的类型参数是接口Comparable和Serializable的子类,根据上述规则,类型擦除后,其实使用的是Compareable类,但是偶尔会在必要的时候,对变量进行强制类型转化,转换为Serializable类。
如果我们把Comparable和Serializable的顺序替换一下呢?那类型擦除后的变量默认就是Serializable。
这里Compareable接口限定子类必须具有方法compareTo,而Serializable其实只是一个标记类,并不具备行为,所以可以推测出,类Interval作为Compareable子类被调用的时候更多一些,所以为了减少类型转换的消耗,把Compareable放在限定类型的第一个,是合理的。
综上,我们可以总结一个规则:撰写java的泛型程序时,为了减少运行的类型转换开销,合理规范类型限定类,最好是最大公共父类或者父接口,当类型参数是多个类和方法的子类时,把使用最多、方法较多的父类型放在第一个位置更好一些。
根据上述规则,我们可以重新审视之前的泛型代码:
其实等同于:
泛型方法也是同理。
这就结束了么?
事情并不是那么简单,泛型类本身也是可以被继承的,比如如下的例子:
如果只进行类型擦除,那么该类会变为:
一方面,DateInterval自己的方法被擦除了类型但是保留了参数的类型,另一方面,继承了被擦除类型的MyPair的方法,两者因为函数的签名不一致,在JVM都是有效的。
这个时候,类型擦除后的代码逻辑和类的多态机制存在了冲突,需要编译期层面对这种场景进行处理,编译器会生成相应的桥方法,对于setFirst方法,编译器生成方法setFirst(Objectfirst)然后在这个桥方法中调用上述定义的setFirst方法。
总之,需要记住有关Java泛型转换的事实:
很显然,如果不是继承自Object的类型,是无法使用泛型特性的。基本类型甚至不是类,自然不能被用来实例化泛型类。
因为运行时,只看得到是Object或者限定类。
禁止使用newPair[10]这种初始化参数化类型的数组。
由于类型擦除,这种声明和Object[]没有区别,会导致数组的类型检查功能失效。
由于类型参数会被替换成Object,那么newT()这种声明方式自然是不会达到预期目的的,作为替代方案,我们可以在泛型类中定义一个构造器表达式:
其中Supplier是一个函数式接口,表示一个无参数且返回类型是T的函数。
也是因为类型擦除
总的来看,由于类型擦除机制,引入了如此多的问题,本质都是同一个问题:虚拟机并不知道你传入的类型是什么。
很难说java的泛型设计能否算一个优秀的设计。
Go语言选择了类似C++的泛型机制,也就是真正的泛型,在编译期实实在在的编译出针对不同类型的逻辑。
在Go1.18的版本里,正式默认开放了泛型的使用,为此也引入了一些新的概念,我们可以列举一下:
写一个简单的go泛型的demo:
以上代码中,Slice是一个自定义的类型,和普通的类型定义不同,带有中括号,中括号内包含多个部分:
//一个泛型接口(关于泛型接口在后半部分会详细讲解)
typeIPrintData[Tint|float32|string]interface{
Print(dataT)
}
//一个泛型通道,可用类型实参int或string实例化
typeMyChan[Tint|string]chanT
通过使用泛型receiver定义泛型方法,我们就可以对泛型类型添加新的通用行为
例如上述定义,就给所有的Slice类型增加了求和统计的方法。
以上就是go泛型的基本使用。相对来说,坑点要比java少一些。
目前go的泛型机制就是依靠编译器生成多份代码,但是目前存在其他方向的演进趋势,值得后续进一步的观察。

主题测试文章,只做测试使用。发布者:最新稳定辅助网,转转请注明出处:https://www.744broad.com/14942.html

(0)
上一篇 2023年3月6日 上午5:51
下一篇 2023年3月6日 上午5:55

相关推荐

  • 二十余年如一梦,此身虽在堪惊-程序猿的悲哀

    二十余年如一梦,此身虽在堪惊-程序猿的悲哀 软件行业摸爬滚打也10年了,刚参加工作的积极热情,到后来海绵式的学习,总相信技术就是一切,历过高潮,逢过低谷,看到过3年ios4w+起步的震撼,也听闻几K没人要的无奈,从开始的挖人2.5倍起,解决家属就业,放开简历电话被打爆再到之后的平常淡化,起起伏伏成了常态,技术历经-从最开始的立志做pc游戏从C、DirectX…

    RUST资讯 2023年2月25日
    40
  • 精品推荐—美玉,秘色瓷,青铜凤尾尊

    精品推荐—美玉,秘色瓷,青铜凤尾尊 和田玉,“中国四大名玉”之一(其三为陕西蓝田玉、辽宁岫玉和河南独山玉)。传统狭义范畴特指新疆和田地区出产的玉石,以和田“子料”为代表闻名于世;广义的和田玉指软玉(真玉)。和田玉虽然因新疆和田而命名,但其本身不是地域概念,并非特指新疆和田地区出产的玉,而是一类产品的名称。中国把透闪石成份占98%以上的石头都命名为和田玉,都在…

    RUST资讯 2023年2月28日
    30
  • 2022 年值得期待的 7 款 Linux 发行版

    2022 年值得期待的 7 款 Linux 发行版 2022年已至,是时候期待一些激动人心的发行版更新了!这里我们挑选了一些值得关注的发行版选择。是时候和2021年说再见了。如果你错过了2021年的Linux大事件,现在你也可以去回顾一下。不过,考虑到2021年已有许多令人印象深刻的发行版推出,2022年,对于较新的Linux发行版以及现有版本的更新,人们的…

    RUST资讯 2023年2月16日
    70
  • 马桶黄垢还骚臭,20多元轻松铮亮反光,懒癌的快乐

    马桶黄垢还骚臭,20多元轻松铮亮反光,懒癌的快乐 今天跟老公大吵一架,气哭了!每次让他分担家务,总是往沙发一躺啥也不干!!!尤其家里的厕所总爱脏黄渍污垢厚厚结了一层,冲也冲不走看着反胃骚臭味都已经弥漫客厅、卧室了,只能自己“硬着头皮”动手!干活太卖力,不小心脏水还往身上溅,感觉真的好恶心每次刚刷干净,过几天又脏了,好累!上次我朋友还吐槽:家里马桶的会好点,她…

  • top进程是否可替换?工具详解

    top进程是否可替换?工具详解 在Linux环境下top命令都不陌生,它以实时动态的方式查看系统的整体运行情况,综合了多方信息监测系统性能和运行信息的实用工具,通过top命令所提供的互动式界面,可以用热键来进行管理。在介绍本篇top命令的替代工具时,我们先来回顾下top的基本语法、常用选项、交互时的热键以及实例解释,从而加深对top的理解,同时也希望能进一步…

    RUST资讯 2023年2月22日
    50
  • 《美国工厂》:根本就不是中国与美国的问题

    《美国工厂》:根本就不是中国与美国的问题 近来大火的奈飞纪录片《美国工厂》,讲述了一个令美国人感到辛酸的故事:代顿市的通用汽车工厂关门,几千工人失业生计无着;几年后来自中国的福耀玻璃进驻了厂区,失业的工人们重新有薪水可以拿。但美国工人的劳动效率和时间,都无法与中国工人相比,厂方的高强度要求和低薪让美国工人非常不适,一度想要成立工会以对抗厂方。厂方的态度是:成…

    RUST资讯 2023年2月13日
    70
  • 优质一级市场项目Interlock重磅来袭!即将发车

    优质一级市场项目Interlock重磅来袭!即将发车 Interlock是一个支持智能和运营安全的数据平台,通过区块链改变网络anquan范式。去中心化安全,激励每个人。Interlock是一个由安全和分析工具组成的生态系统,人们可以通过贡献他们控制的安全和分析数据来赚取INTER代币。我们称这个空间weiDeSec,它是第一个去中心化安全和情报的空间。例如…

    RUST资讯 2023年3月10日
    20
  • 外媒称腾讯投资的Epic今年盈利30亿美元;腾讯宣布健康系统已接入15款热门游戏|葡萄晚报

    外媒称腾讯投资的Epic今年盈利30亿美元;腾讯宣布健康系统已接入15款热门游戏|葡萄晚报 这是一个每日要闻汇总栏目,我们将为您筛选今日业界值得关注的重要新闻,通过一篇文章掌握当日业界动态。美国科技媒体TechCrunch援引知情人士报道称,今年《堡垒之夜》开发商EpicGames盈利30亿美元。同时,由于《堡垒之夜》大获成功,EpicGames公司估值达到…

  • Deno 1.5 使用新编译器,打包性能提升 3 倍

    Deno 1.5 使用新编译器,打包性能提升 3 倍 为JavaScript和TypeScript开发Deno运行时的团队最近发布了Deno1.5。新版本通过使用基于Rust的JavaScript/TypeScript编译器swc来提高打包性能,并通过摇树优化进一步减少包的大小。Deno1.5实现了alert、confirm和prompt等Web平台API,…

    RUST资讯 2023年2月17日
    70
  • Rust 常见的有关生命周期的误解 下篇

    Rust 常见的有关生命周期的误解 下篇 之前我们讨论了Rust对函数的生命周期省略规则。Rust对trait对象也存在生命周期省略规则,它们是:如果trait对象被用作泛型类型的一个类型参数,那么trait对象的生命周期约束会依据该类型参数的定义进行推导若该类型参数有唯一的生命周期约束,则将这个约束赋给trait对象若该类型参数不止一个生命周期约束,则tr…

    RUST资讯 2023年2月20日
    100
关注微信