技术开发 频道

浅谈Java语言的Type Erasure特性

  您可以发现,.NET的IL便确切包含了TKey和TValue的类型信息。而在运行的时候,CLR会为不同的泛型类型生成不同的具体类型代码,这在我之前的文章中也有所提及。

  那么,Java和C#两种泛型实现方式分别有什么优势和劣势呢?Java这种Type Erasure做法,最大的优势便在于其兼容性:即便使用了泛型,但最后生成的二进制文件也可以运行在泛型出现之前的JVM上(甚至JDK中不需要添加额外的类库)——因为这里的泛型根本不涉及JVM的变化。而.NET中的泛型需要CLR方面的“新能力”,因此.NET 2.0的程序集是无法运行在CLR 1.0上的——当然.NET 1.0的程序集可以直接在CLR 2.0上执行。而CLR实现方式的优势,便在于可以在运行期间体现出“模板化”的优势。.NET程序员都知道,泛型可以节省值类型的装箱和拆箱的开销,即便是引用类型也可以避免额外的类型转化,这些都能带来性能上的提高。

  因此,在.NET社区经常会把Java的这种实现方式称之为“假泛型”,而同时也会有人反驳到:泛型本来就是语言上的概念,实现不同又有什么关系,凭什么说是“假”的呢?其实,由于失去了JVM的支持,一些.NET平台上常用的,非常有效的开发方式都难以运用在Java上。例如所谓的泛型字典:

浅谈Java语言的Type Erasure特性

  由于Cache在运行时是个具体独立的类型,因此泛型字典是性能最高的存储方式,比O(1)时间复杂度的哈希表还要高出许多。如果说这也只是运行方面的优势,那么这段代码中的“泛型工厂”代码(即Factory.Create(),包括类似的Factory.Create()这种)则是Java语言中无法实现的。这是因为Type Erasure的作用,在运行时JVM已经丧失了TKey这样的类型信息,而在.NET平台上,TKey则是Create签名的组成部分。

  Type Erasure造成的限制还有不少,如果您是一个C#程序员,可能难以相信以下的Java代码都是不合法的:

浅谈Java语言的Type Erasure特性

  猜猜看,如果value变量是个HashMap[Int, Object]类型的对象,上面的代码会输出什么结果呢?如果是C#或是F#这样运行在.NET平台上的语言,最终输出的一定是“Value is not ...”。只可惜,由于JVM的Type Erasure特性,以上代码输出的却是“Value is HashMap[String, Int]”。这是因为在运行期间JVM并不包含泛型的类型信息,HashMap[K, V]即是HashMap,无论HashMap[String, Int]还是HashMap[Int, Object]都是HashMap,JVM无法判断不同泛型类型的集合之间有什么区别。不过还好,Scala编译器遇到这种情况会发出警告,程序员可以了解这些代码可能会出现的“误会”。

  因此,为什么有IKVM.NET这样的项目可以将Java语言编译成.NET程序集(也可以将Java的jar包转化成.NET程序集),却没有项目将C#编译到JVM上(或是将C#程序集转化为jar包)。这是因为,JVM不足以支撑C#语言所需要的所有特性。而从运行时的中间代码角度来说,JVM Bytecode的能力也是.NET IL的子集——又有什么办法可以将超集塞入它的子集呢?

  此外,如CLR的值类型可能也很难直接落实在JVM上,这也是JVM上运行C#的又一阻碍。

  当然,如果真要在JVM上实现完整的C#也并非不可以。只要在JVM上进行一层封装(例如还是就叫做CLR,CLR Language Runtime),一定可以满足C#的全部要求。但是这个代价太高,即使实现了这点可能也没什么实际意义。而事实上,已经有人在JVM上实现了一个x86模拟器,那么又有是做不了的呢?实在不行,我们就在模拟器上装一个Windows操作系统,然后装一个Microsoft .NET,再……

0
相关文章