using System; using System.Collections.Generic; // let's look at a generic class in C# and compare with the similar one in Java: class generalclass where B:IComparable //where A:B { A x; B y; A[] ar; void f() { //y = (B)x; // compiled with warning in Java, COMPILER ERROR here //x = (A)y; // but will comple with declaration where A:B // no generic array creation in java without workaround: //ar = (A[])new object[10]; // compiler ERROR in C#, casting not allowed. ar = new A[10]; // But direct generic array creation is fine! List al = new List(); // also ok, List equivalent to ArrayList }// public static A z; // not a problem! public B largest(B[] R) // find largest B value in array R: { B max = R[0]; for(int i=1;i0) max = R[i]; return max; }// this really is ONE polymorphic, type checked procedure, unlike in C++ // does overloading work with generic variables? (QUIZ!) public int h(A x) { return 1; } public int h(B x) { return 2; } // C# allows this overload to compile but... }// a java generic class and it's problems. public class generics { public static void Main() { generalclass n = new generalclass(); //int x = n.h("abc"); // which h is called? this is a COMPILER error }//main } /* Most of the differences between the behaviors of this program and the Java program is explained by the fact that C# does not use type erasure and change the type of x to object at runtime. So generalclass and generalclass REMAIN DISTINGUISHABLE at runtime. Each class constructor is created with hidden arguments that's instantiated at runtime. So new generalclass, for example, with instantiate A with int and B with string. There's no need to allow the cast (B)x because it might possibly work: it should be rejected because there's no compile-time information to justify it: A and B are statically unrelated classes unless we establish, as part of the generic class, that A:B (A is a subtype of B). Similarly, there's no need to allow the cast from object[] to A[] just because it might work There's no problem with declaring a static variable of generic type because each runtime class will still have it's own copy of the variable. However, the reason generic array creation A[] x = new A[10]; works is due to a hidden use of an interface. Generic arrays are treated in the same way as regular arrays in C#: they are actually objects that implements the interface Ilist. So when we create generic arrays, it's like we're creating something like the List class underneath (except it can't be resized). C# allows operator overloading, including the indexing operation [i], so syntactically it still feels like we're using a regular array, but it isn't. To proof this point: consider the following class: */ class test where B:A // B is a subclass of A { void g() { object[] x = new string[10]; // we know this compiles but shouldn't //A[] z = new B[10]; //this should compile too then, right? because B:A? // But it's a compiler error, the same type of error that we get with: // List l = new List(); // This is because in IList and List, A is INVARIANT. } } /* As you can see, the differences between these languages are all a little subtle, and certainly requires you to think hard and look far behind the syntax. We've seen three versions of a feature which syntactically look very much alike one another but which hold deep differences underneath. C++ templates are more of a convient way to copy and paste. It does not type check the generic code itself: only those classes that instantiate it. Furthermore, it can't create a single polymorphic procedure that works for all types. Both Java and C# generics are vast improvements upon these problems. But there are also signifcant differences between Java and C# generics. Java uses type erasure, and this has important consequences, including unchecked type casts that are allowed, whereas C# disallow these casts unless the relation (such as declared by where B:A) is known at compile time. So Java type-checks the generic variables at compile time, but only up to a point. C# does not use type-erasure. The type that a generic variable is instantiated with is known at runtime. This means that we need not allow unsafe type casts to allow certain operations. Also, C# "cheats" by syntactically hiding the difference between generic arrays and ordinary arrays, although one can still reveal a difference with careful testing. Languages like Kotlin and even the much more advanced Scala that compile into the java virtual machine all inherit type erasure and some of its weaknesses. However, they try to alleviate it with other means. All arrays in Kotlin are invariant: basically this means you can only use ArrayList instead of "regular" arrays, although the array syntax is retained like in C#. Static variables are replaced in Kotlin by something called a "companion object", which is a single object that's created for each class. #### Finally, there is an issue that could potentially cause misunderstanding. Can you overload functions with a single generic variables? public int h(A x) { return 1; } public int h(B x) { return 2; } // C# allows this overload to compile but... If you search the web, there are people who say that this code "will not compile in java but will compile in C# because java uses type erasure so at runtime both A and B are of type Object." THIS IS A BUNCH OF CRAP. It's written by people who fundamentally misunderstand the difference between overloading and overriding. Overloading resolves function calls at compile time and therefore has NOTHING TO DO WITH TYPE ERASURE. It's not allowed because you can write (look at main above): generalclass n = new generlclass(); int x = n.h("abc"); // this will cause a COMPILER ERROR in C# also. There's no way for the compiler to know in the above situation, how to resolve which version of h to call. Java does not allow the class with the two versions of h to compile. C# chooses to compile the class, but not the code that tries to use it in an ambiguous way. But this is more of a design choice than a fundamental difference: in both languages all calls to overloaded functions are resolved at COMPILE TIME. Overloading is not an essential feature and issues it causes can easily be avoided by: public int h1(A x) { return 1; } public int h2(B x) { return 2; } // all problems solved. */