The essential difference between generics in C # and Java

Source: Internet
Author: User
Tags mscorlib

Turn from: Why there is no C # language on the JVM. On the characteristics of type erasure

Every time I talk about languages, I can't help but scold Java as a poor, complacent language--an estimate that waits until the Java language is replaced by other languages on the JVM. There are already many languages on the JVM: Jruby,jython, and some language specific to the JVM platform, such as Scala and groovy, and so on. However, why is there no C # language on the JVM? Arguably, the door is very similar to Java, but powerful and many languages are more easily accepted by Java programmers. You might say that sun and Microsoft are rivals, how can C # be ported to the JVM platform. Well, it makes sense, but there's no one in the community to do it (know that other languages on the JVM are initiated by the community). In fact, in my opinion, this is still subject to technical constraints.

Generics are an important feature of the Java and C # languages, making it easy for programmers to type-secure programming without the need for constant type conversion as before. For example, we would do this by writing the encapsulation of a generic dictionary in 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);
    }
}

Doesn't seem to be any different from C #, does it? However, if we look at the compiled bytecode (similar to the. NET platform, you'll find a hint of wonder. The result of using Javap-c dictwrapper is that

Compiled from ' Dictwrapper.java ' public class Jeffz.practices.DictWrapper extends java.lang.object{public
  Jeffz.practices.DictWrapper (); code:0: Aload_0 1:invokespecial #1; Method Java/lang/object. " <init> ":() V 4:aload_0 5:new #2; Class Java/util/hashmap 8:dup 9:invokespecial #3; Method Java/util/hashmap. " <init> ":() V 12:putfield #4;
   Field M_container:ljava/util/hashmap;
  15:return public Java.lang.Object get (java.lang.Object); code:0: Aload_0 1:getfield #4;
   Field M_container:ljava/util/hashmap; 4:aload_1 5:invokevirtual #5;
   Method Java/util/hashmap.get: (ljava/lang/object;) Ljava/lang/object;
  8:areturn public void put (Java.lang.Object, java.lang.Object); code:0: Aload_0 1:getfield #4;
   Field M_container:ljava/util/hashmap; 4:aload_1 5:aload_2 6:invokevirtual #6; Method Java/util/hashmap.put: (Ljava/lang/object;
   Ljava/lang/object;) Ljava/lang/object; 9:pop 10:return}

As you can see from the bytecode, it does not contain any information related to k,v. The parameters and return values of the Get/put method are both object types, even built-in hashmap. So how does calling Dictwrapper's code do "strong typing"? For example:

public static void Main (string[] args) {
    dictwrapper<string, string> dict = new dictwrapper<string, STRING&G t; ();
    Dict.put ("Hello", "World");
    String World = Dict.get ("Hello");
}

Its bytecode is:

public static void Main (java.lang.string[]);
  Code:
   0:	new	#2//class jeffz/practices/dictwrapper
   3:	dup
   4:	invokespecial	#3; Method Jeffz/practices/dictwrapper. " <init> ":() V
   7:	astore_1
   8:	aload_1
   9:	LDC	#4;//string Hello
   11:	LDC	#5//string World
   :	invokevirtual	#6//method jeffz/practices/dictwrapper.put: ( Ljava/lang/object; Ljava/lang/object) V
   : aload_1:	LDC	#4;//string Hello
   :	invokevirtual	#7;//method jeffz/practices/dictwrapper.get: (ljava/lang/object;) Ljava/lang/object;	checkcast	#8//class java/lang/string: astore_2: Return
}

See that line of code labeled 22 is not. This checkcast instruction converts the result of the previous sentence invokevirtual to a string type--dictwrapper.get returns the most common object.

This is the generic implementation of the Java language--note that I'm talking about the Java language here, not the JVM. Because the JVM itself does not have the concept of "generics," the generics of the Java language are completely compiler magic. The generic code we write is actually dealing with object objects, and the compiler is helping us eliminate the redundant type conversion code, which guarantees the type safety at the code level. Because the type information of all generics is stripped at run time, this generic implementation is called type Erasure (Types erase).

In. NET, it is completely different, "generics" is true to implement the functionality on the CLR level. For example, the IL code for the Dictwrapper.get method on. NET is:

. method public Hidebysig instance! TValue Get (! TKey key) cil managed
{
    . maxstack 2
    . Locals init (
        [0]! TValue cs$1$0000)
    l_0000:nop 
    l_0001:ldarg.0 
    l_0002:ldfld class [mscorlib] ... Dictionary ' 2 ... Dictwrapper ' 2::m_container
    l_0007:ldarg.1 
    l_0008:callvirt instance! 1 [mscorlib] ... Dictionary ' 2::get_item (!0)
    l_000d:stloc.0 
    l_000e:br.s 
l_0010 l_0010:ldloc.0 }

You can find that. Net Il exactly contains the type information for TKey and TValue. At run time, the CLR generates different specific types of code for different generic types, as I mentioned in my previous article.

So, what are the advantages and disadvantages of the two generic implementations of Java and C #, respectively? Java This type erasure practice, the biggest advantage is its compatibility: Even with generics, the resulting binaries can run on JVMs prior to generics (even the JDK does not need to add additional class libraries)--because generics do not involve JVM changes at all. Generics in. NET require the "new capabilities" of the CLR, so that. NET 2.0 assemblies cannot run on CLR 1.0--Of course. NET 1.0 's assemblies can be executed directly on CLR 2.0. The advantage of the CLR implementation is that it can embody the advantages of "templating" during the operation. NET programmers know that generics can save the cost of boxing and unboxing of value types, and that even reference types can avoid additional type conversions, which can lead to performance improvements.

Therefore, in the. NET community it is often called the Java implementation as a "fake generics", but it is also countered that the generic is the language concept, what is the relationship between the implementation of the different, and what is the "false"? In fact, due to the loss of the JVM's support, some. NET platform, very effective development methods are difficult to use in Java. For example, the so-called generic dictionary:

public class Cache<tkey, tvalue>
{public
    static TValue Instance;
}

public class Factory
{public
    static string create<tkey> ()
    {
        if (Cache<tkey, String>. Instance = = null)
        {
            cache<tkey, string>. Instance =//Some expensive computation
        } return

        Cache<tkey, String>. Instance
    }
}

Because Cache<tkey> is a specific, stand-alone type at run time, the generic dictionary is the most performance-efficient storage method, much higher than the O (1) Time complexity hash table. If this is also just a running advantage, then the "Generic Factory" code in this code (that is, factory.create<sometype> (), including a similar factory<t>. Create () is not possible in the Java language. This is because of the role of type Erasure, where the JVM has lost type information such as TKey at run time, while the. NET platform, TKey is part of the create<tkey> signature.

There are a lot of restrictions on Type erasure, and if you're a C # programmer, it may be hard to believe that the following Java code is illegal:

public class Myclass<e> {public
    static void MyMethod (Object item) {
        if (item instanceof E) {//Compiler ER Ror ...
        }
        E item2 = new E (); Compiler error
        e[] IArray = new E[10];//Compiler Error
    }
}

Because the JVM does not provide support for generics, the pressure on the JVM to support generics, such as Scala, falls completely on the compiler. Also, because these languages are based on the JVM, Type erasure affects almost all languages on the JVM platform. In Scala, for example, its pattern-matching syntax can be used to determine the type of a variable:

Value Match {case
    x:string => println (' value is a String ') case
    x:hashmap[string, Int] => println (' value is hashmap[string, int] "") Case
    _ => println ("Value isn't a String or hashmap[string, Int]")
}

Guess what the above code will output if the value variable is an object of type Hashmap[int, object. If it is C # or F #, it runs in the. NET platform, the final output must be "Value is not ...". Unfortunately, because of the type erasure attribute of the JVM, the above code outputs "Value is hashmap[string, Int]". This is because the JVM does not contain generic type information during runtime, Hashmap[k, V] that is HashMap, regardless of hashmap[string, Int] or hashmap[int, Object] is HashMap, The JVM cannot tell what the difference is between collections of different generic types. Fortunately, the Scala compiler will warn you of this situation, and the programmer will be able to understand the "misunderstanding" that might arise from the code.

So why are there ikvm.net projects that can compile the Java language into a. NET assembly (or convert a Java jar package into a. NET assembly) without a project compiling C # into a JVM (or converting a C # assembly into a jar package). This is because the JVM is not sufficient to support all the features required by the C # language. The JVM's ability to bytecode is also a subset of. NET IL from the runtime's intermediate code--and what is the way to cram a superset into its subset?

In addition, the value types of the CLR may also be difficult to implement directly on the JVM, which is another impediment to running C # on the JVM. Because of these factors, I want to be like F #. NET language is also almost impossible to appear on the JVM.

Of course, if you really want to implement full C # on the JVM, it's not an easy bet. A layer of encapsulation on the JVM (for example, or CLR,CLR Language Runtime) can satisfy all of C # 's requirements. But the price is too high, and even if it does, it may not make sense. As a matter of fact, someone has implemented a x86 simulator on the JVM, so there's nothing you can do about it. No, we're going to install a Windows operating system on the simulator, then install a Microsoft. NET, and then

Related Article

Contact Us

The content source of this page is from Internet, which doesn't represent Alibaba Cloud's opinion; products and services mentioned on that page don't have any relationship with Alibaba Cloud. If the content of the page makes you feel confusing, please write us an email, we will handle the problem within 5 days after receiving your email.

If you find any instances of plagiarism from the community, please send an email to: info-contact@alibabacloud.com and provide relevant evidence. A staff member will contact you within 5 working days.

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.