As you may know I am a kind of programming language geek. Despite the fact that every software developer uses one or more programming languages most developers do not care much about them. Some are interested in software design patterns, some care more about development methodologies, some are algorithm guys, some like diving deep into databases, many are just interested in technologies and frameworks and there are even those who do not have a real passion but work as developers because this is how they pay the bills. Well my passion are programming languages and while I do not have the opportunity to work professionally on a compiler or even a parser I try to follow the development of popular programming languages and enjoy reading the motivation for language design decisions and compiler implementation decisions that language developers post online. I also like digging in programming language history.
As part of my passion I regularly take part in IT flame wars on forums, blogs, IRC and in the physical world. One discussion that repeats often is Java vs. C#. I decided to summarize my arguments in this series of posts and just link them next time instead of writing the same thing again. What is more Java and C# share similar philosophy which makes comparison relatively easy. They are both statically typed, built on managed runtimes, object oriented, JIT compiled, etc. This eliminated many of the religious arguments and allows me to focus on more concrete things. When reading keep in mind that this article compares the programming languages. It does not deal with political issues like "Microsoft vs. Open Source" and only touches on things like tools and the underlying framework where it is directly related to the programming language or the compiler without taking into account the price, quality, portability, vendor support and availability of tools, libraries, web frameworks, desktop UI frameworks, mobile platforms support and many others that can make the platform better or worse for your particular case and are usually more important than the language itself. I am planning to only focus on features that both C# and Java have in some way. I will not comment on features that C# has and Java lacks and why they should be included in the language. It seems best to wait until Java 7 before comparing these features because many of them are proposed to be included in this future version. Probably these features will be discussed in future articles.
I would also like to state that I do not consider Java language designers stupid or incompetent in any way. Many of the mistakes in Java were avoided in C# only because they were made in Java and the result was evident in the wild. Some of the decisions made sense when Java was made because it was created in a different time and targeted a different group of developers. In fact the Java language designers have done wonders introducing new features provided with the current legacy.
It is also important to agree on what we consider good and bad and what is the main goal of languages like C# and Java. Performance is important but not of extreme importance because otherwise we would just use C/C++. It is good to have shorter code with less negotiations with the compiler but not of extreme importance otherwise we would use a dynamically typed language. It is good to have easily readable code but not of extreme importance otherwise we would use Python or Delphi. In my opinion the most important goal of C# and Java is producing the most maintainable code while staying as close to the aforementioned goals as possible.
So here we go...
Java's primitive types suck. Java books like to point out that in Java everything is an object and then quickly introduce the exception - the primitive types. The very fact that there is an exception increases the complexity of the language and creates problems for people using it. Even professional Java programmers have troubles with wrapping and unwrapping ints into Integers especially when they need to compare wrapped and unwrapped int. Pared with the lack of operator overloading it often requires thinking what the comparison operator does. I know that most Java developers will be able to predict the output of the following code but I doubt many of them will claim that this behavior improves readability and maintainability:
Integer i1 = 1;
int i2 = 1;
System.out.println(i1 == i2);
Integer i3 = new Integer(1);
Integer i4 = new Integer(1);
System.out.println(i3 == i4);
Why does the first comparison choose to unbox the Integer instead of boxing the int? Why does the second comparison operator change the semantics if Integer and int are supposed to be the same?
Even with autoboxing wrappers are all over the Java source code. You cannot declare a generic class or a generic interface of a primitive type which can hurt the performance in cases where extreme performance is required. When a type system needs two different types to represent a 32 bit integer then you know something is really wrong.
C# solves these problems by introducing a special kind of objects - the value types. Value types do inherit from object but are passed around by value instead of reference and are stored on the stack instead of the heap (for local variables and method arguments but not for class members). Autoboxing occurs when these types are cast to object or object is cast to these types (autounboxing) but this is transparent to the user. Wrapper types are never seen and are not present in the type system despite the fact that the runtime wraps value types internally. Generics of value type are allowed and optimized by the JIT compiler to avoid boxing. In my opinion the power and beauty of C#'s type system approach is best demonstrated by this expression illegal in Java but legal in C#:
5.ToString()
The fact that you can call a method inherited from object on an int literal shows that in C# everything is really an object and this is transparent to the developer. There are user defined value types which are useful for many more things. For an example of elegant use you can read Jon Skeet's article on
custom value types. What is even more interesting is that he used custom value types when porting a Java library.
Update:
Part 1 has been published.