爲何JVM上沒有C#語言?淺談Type Erasure特性

JVM上目前已經有許多語言了:JRuby,Jython;還有一些特定於JVM平臺的語言,如Scala和Groovy等等。可是,爲何JVM上沒有C#語言呢?按理說,這門和Java十分類似,卻又強大許多的語言更容易被Java程序員接受纔對。您可能會說,Sun和微軟是對頭,怎麼可能將C#移植到JVM平臺上呢?嗯,有道理,可是爲何社區裏也沒有人這麼作呢(要知道JVM上其餘語言都是由社區發起的)?其實在我看來,這仍是受到了技術方面的限制。
泛型是Java和C#語言的重要特性,它使得程序員能夠方便地進行類型安全的編程,而不須要像之前那樣不斷進行類型轉換。例如,咱們要在Java中寫一個泛型字典的封裝即可以這麼作:
public class DictWrapper {程序員

    private HashMap<K, V> m_container = new HashMap<K, V>();編程

    public V get(K key) {
        return this.m_container.get(key);
    }安全

    public void put(K key, V value) {
        this.m_container.put(key, value);
    }
}app

看上去和C#並無什麼區別,不是嗎?不過,若是咱們觀察編譯後生成的bytecode(相似於.NET平臺上的IL),便會發現一絲奇妙之處。
那麼,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上。例如所謂的泛型字典:性能

public class Cache<TKey, TValue>
{
    public static TValue Instance;
}this

public class Factory
{
    public static string Create<TKey>()
    {
        if (Cache<TKey, string>.Instance == null)
        {
            Cache<TKey, string>.Instance = // some expensive computation
        }code

        return Cache<TKey, string>.Instance;
    }
}
因爲Cache<TKey>在運行時是個具體獨立的類型,所以泛型字典是性能最高的存儲方式,比O(1)時間複雜度的哈希表還要高出許多。若是說這也只是運行方面的優點,那麼這段代碼中的「泛型工廠」代碼(即Factory.Create<SomeType>(),包括相似的Factory<T>.Create()這種)則是Java語言中沒法實現的。這是由於Type Erasure的做用,在運行時JVM已經喪失了TKey這樣的類型信息,而在.NET平臺上,TKey則是Create<TKey>簽名的組成部分。
猜猜看,若是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#的又一阻礙。因爲這些因素存在,我想如F#這樣的.NET語言也幾乎不可能出如今JVM上了。
 對象

相關文章
相關標籤/搜索