Sunday, July 9, 2017

C# vs Java : Generics

Generic classes look the same in both Java and C#:
public class Box<T> {
 private T t;

 public T get() {
  return t;
 }

 public void set(T t) {
  this.t = t;
 }
}
C# allows you to have a generic and non-generic class that have the same name and are both in the same namespace. Java, however, does not allow this. Why is this?

Recall that when the Java compiler compiles a generic class it goes through a process known as type erasure. Basically what that means is the compiler erases the type parameters in the generic class and replaces them with references of type Object. In other words here’s what the above generic Box class looks like after its been compiled by the Java compiler:
public class Box {
 private Object t;

 public Object get() {
  return t;
 }

 public void set(Object t) {
  this.t = t;
 }
}
Therefore it makes sense that Java would not allow you to have a generic and non-generic class that have the same name and are both in the same package. So why, then, does C# allow it? Let’s go back to Java and observe that the following two statements...
System.out.println(new Box<String>().getClass());
System.out.println(new Box<Integer>().getClass());
...both produce the same output.
class Box
But when we do this in C#…
Console.WriteLine(new Box<String>().GetType());
Console.WriteLine(new Box<int>().GetType()); 
...we get a very different result.
Box`1[System.Int32]
Box`1[System.String]
When you compile a program in .NET that uses generics additional information gets stored in the executable. The JIT (Just In Time) compiler then takes this information and produces a specialized version of the generic class just for that type. It’d be as if you had coded the following in your program:
public class Box1Int32 {
 private int t;

 public int get() {
  return int;
 }

 public void set(int t) {
  this.t = t;
 }
}

public class Box1String {
 private String t;

 public String get() {
  return t;
 }

 public void set(String t) {
  this.t = t;
 }
}
Actually its a bit more complicated then that. If the generic class is going to be used with a reference type then only one version of the specialized generic class really needs to be created, regardless of the type actually being used. That is, the following two lines of code...
new Box<String>();
new Box<Klass>();
...would essentially use the same (JIT generated) class.
public class Box1Object {
 private Object t;

 public String get() {
  return t;
 }

 public void set(Object t) {
  this.t = t;
 }
}
Value types, on the other hand, are different. That’s because the size of value types vary whereas the size of a pointer to a reference type remains the same. Therefore a specialized version of the generic class has to be created for each value type that’s used.

The following two statements...
new Box<int>();
new Box<long>();
...would cause the JIT compiler to create two versions of the generic Box class:
public class Box1Int32 {
 private int t;

 public int get() {
  return t;
 }

 public void set(int t) {
  this.t = t;
 }
}

public class Box1Long {
 private long t;

 public long get() {
  return t;
 }

 public void set(long t) {
  this.t = t;
 }
}
Notice that C# allows you to use primitive types when instantiating instances of a generic class.
// works in C#, but not Java
new Box<int>(); 

Both Java and C# allow you to declare a static method inside of a generic class. The difference between the two is that in Java you have to use a different type parameter than what the generic class uses.
public class Klass<T> {

   // works in C#, but not Java
   public static void foo(T t) {
      Console.WriteLine(t.GetType()); 
   }    
}
As a consequence of this, when you use a generic static method that's in a generic class you have to specify a type for the generic class.
public static void Main() {  

   // compiler error
   // Klass.fooBar("salut");

   Klass<long>.fooBar("Hello World");
}
Here’s how to declare a generic static method that's inside of a generic class in Java:
public class Klass<T> {
   
   public static <U> void fooBar(U u) {
      System.out.println(u.getClass());
   }
}
Because of this, Java let's you use a generic static method that's in a generic class without having to specify a type for the generic class.
public static void main(String... args){

   Klass.fooBar("Hello World");

   // following 2 statements result in a compiler error
   // Klass<Long>.fooBar("salut");
   // Klass.<Long>fooBar("salut");
}
To declare the same method in C# the type parameter is placed after the method’s name:
public class Klass<T> {

   public static void bar<U>(U u) {
      Console.WriteLine(u.GetType()); 
   }
}

C# does not have Java’s raw types. That is, in Java you can use a generic class or interface without specifying a type parameter.
// works in Java, not C#
Box box = new Box(); 

C# does not have Java’s diamond operator.
Box<Integer> ibox;

// works in Java, but not C#
ibox = new Box<>(); 

You can assign a default value to variables of the generic type in both languages but they limit you to what that default value can be.

In Java its null.
// Java
private T t = null;
Since C# allows both reference types and values types as the type of the generic, you can’t just assign it the value null. Instead you have to use the default keyword, like so:
private T t = default(T); 

Both languages allow you to put restrictions on the types that can be used with the generic class.

Recall that Java allows you to restrict the generic's parameter to a specific type or a subtype of that type. This is done using the extends keyword like so:
public class Parent {
}

public class Child extends Parent {
}

public static <T extends Parent> void fooBar(T t) {
}
public static void main(String[] args) {
   fooBar(new Parent());
   fooBar(new Child()); // subtype, okay

   fooBar(new Object()); // supertype of Parent, NOT okay
   fooBar(new String()); // neither are non-related classes
}
In C# you would use the where keyword
public class Child : Parent {
} 

public static void fooBar<T> (T t) where T : Parent {
}
Here’s what constraints look like on the class level, starting with Java:
public class Box<t extends Parent> {
   // ...
Here’s the C# equivalent:
public class Box<T> where T : Parent {
   // ...
C# goes even one step further with its new constraint. (pun not intended)
public class Box<T> where T : new() { 
 // ….
Now the only types that are allowed to be used with this generic class are those that have a parameter-less constructor
public class Good {
}

public class Bad {
   public Bad(int x) {
   }
} 
Box<Good>();

// compiler error
Box<Bad>(); 
Because of this constraint, we can instantiate new instances of the type inside of the generic class.
public class Box<T> where T : new() {
   private T t;

   public T get() {
      return t;
   }

   public void set(T t) {
      this.t = new T();
   }
}
The new constraint also says that abstract types are not allowed either.
public abstract class MyAbstract {
} 
// compiler error
new Box<MyAbstract>();
public interface MyInterface {
} 
// compiler error
new Box<MyInterface>(); 

In Java, though, you can even restrict the type parameter to a specific type or a super type of that type. (C# doesn't have this feature).
public static void fooBar(Box<? super Number> box) {
}
public static void main(String[] args) {
   fooBar(new Box<Object>());
   fooBar(new Box<Number>());

   fooBar(new Box<Integer>()); // compiler error: Integer is subtype of Number
}

You can specify multiple bounds for a type parameter. Here's what that looks like in Java:
public class MyClass {
}

public interface MyInterface {
}
public static <T extends MyClass & MyInterface> void fooBar(T t) {
}
Here’s the equivalent C#:
public void fooBar<T>(T t) where T : MyClass, MyInterface {
} 
Notice that when the bound is a class, you specify it before the bounds that are interfaces. Failing to do so results in a compiler error.
// compiler error Java
public static <T extends MyClass MyInterface> void fooBar(T t) {
}
Same thing is true in C#.
// compiler error C#
public static void fooBar<T>(T t) where T : MyInterface, MyClass {
} 
Here’s how to specify multiple interfaces, starting with Java:
public interface MyOtherInterface {
}
public static <T extends MyClass & MyInterface & MyOtherInterface> void fooBar(T t) {
}
Here’s the C# version:
public static void fooBar<T>(T t) where T : MyClass, MyInterface, MyOtherInterface {
} 

Besides classes and methods, interfaces can be made generic as well.
public interface MyInterface<T> {
}
Whenever a class implements an interface that is declared as being generic, that class itself must also be declared as generic.
public class Correct<T> implements MyInterface<T> {
}

// compiler error in Java
public class Wrong implements MyInterface<T> {
}
Here’s the C# version:
public class Correct<T> : MyInterface<T>{
} 

// compiler error
public class Wrong : MyInterface<T>{
} 
There is an exception to this rule and that's when you specify the type parameter for the generic interface that your class is implementing. For such cases you don't declare the class as being generic.
// Java
public class MyClass implements MyInterface<Double> {
}
// C#
public class MyClass : MyInterface<double> {
} 

References

"Generics in C#, Java, and C++" by Bill Venners with Bruce Eckel (January 26, 2004)
Generics in the Run Time (C# Programming Guide)
new Constraint (C# Reference)
default Keyword in Generic Code (C# Programming Guide)
Comparing Java and C# Generics - Jonathan Pryor's web log