Monday, November 26, 2007

dirkmeister.de down

Due to a hard disk crash on the server dirkmeister.de is down. During the next days I will try to bring it back. I hope that I can rescue the old articles.

Thursday, November 01, 2007

Vergleich von Generics in Java und C#

Jonathan Pryor vergleicht in dem Artikel "Comparing Java and C# Generics" die Umsetzung von Generics in Java und C#.
Er erklärt dabei die Gemeinsamkeiten und Unterschiede in Syntax und vor allem Semantik. Dabei geht er auch auf die weniger bekannten Funktionen wie die Möglichkeit weitere Typbedingungen zu definieren (in Java z.B. "T extends Number & Comparable<T>", in C# z.B. "where T : Comparable<T>") und Typwildcards ein.
Der größe Unterschied zwischen Java- und C#-Generics ist, dass Java-Generics ausschließlich zur Compile-Zeit verfügbar sind. Zur Laufzeit sind Genericinformationen nicht verfügbar.

Der Grund für dieses "type erasure " ist, dass man bei Java 5.0 Änderungen an dem Classfile-Format und der Java Virtual Machine absolut vermeiden wollte (eine Anforderung, die man am Ende doch nicht durchhalten konnte) und es außerdem eine einfache Zusammenarbeit von generifizierten Code und Legacy-Code erlaubt.
Dies klingt erst einmal harmlos, aber wie Jonathan begründet, muß man sich dieser Eigenschaft bewußt sein, wenn man generifizierten Code schreibt. Das zentrale Beispiel für einen der Fallstricke ist:

HashSet<String> set1 = new HashSet<String>();
HashSet<Integer> set2 = new HashSet<Integer>();
set1.equals(set2); // ist wahr
set1.getClass().equals(set2.getClass()); // ist auch wahr
Während man mit dem Ergebnis des 1. Vergleiches noch rechnen kann, überrascht das Ergebnis des 2. Vergleiches doch sehr. Es gibt in Java keine Möglichkeit zur Laufzeit Klassen auf Grund von generischen Typinformationen zu unterscheiden. Sobald die Typprüfungen des Compilers z.B. durch Reflektion umgangen werden, wird man bei falscher Verwendung wieder ClassCastExceptions erhalten genauso wie "früher". Bei C++ und C# bleiben die Typinformationen auch zur Laufzeit erhalten.

Sein Fazit gibt er schon in dem Untertitel bekannt. Er lautet "Whats Wrong With Java Generics":

The generics capabilities in Java and .NET differ significantly. Syntax wise, Java and C# generics initially look quite similar, and share similar concepts such as constraints. The semantics of generics is where they differ most, with .NET permitting full runtime introspection of generic types and generic type parameters in ways that are obvious in their utility (instance creation, array creation, performance benefits for value types due to lack of boxing) and completely lacking in Java.In short, all that Java generics permit is greater type safety with no new capabilities [...].Contrast this with C#/.NET, where the above code isn’t possible, as there are no raw types, and converting a List to a Listwould (1) require an explicit cast (as opposed to the complete lack of casts in the above Java code), and (2) generate an InvalidCastException at runtime from the explicit cast.Furthermore, C#/.NET convey additional performance benefits due to the lack of required casts (as the verifier ensures everything is kosher) and support for value types (Java generics don’t work with the builtin types like int), thus removing the overhead of boxing, and C# permits faster, more elegant, more understandable, and more maintainable code.

Ich sehe nur einen praktischen Vorteil der Java-Umsetzung. Dort hat man nicht die Mühe geschaut, die bestehende Bibliothek zu "generifizieren". Damit ist gemeint, dass bestehende Klassen die Möglichkeiten von Generics ab Java 5.0 intensiv nutzen. Die bekannteste Anwendung sind zweifelslos die Collections-API, aber auch das Comparable-Interface, ThreadLocal und (interessanterweise) die Klasse Class nutzen Generics intensiv.

Bei C# hat man keine bestehende Klasse angepasst. Die neuen Möglichkeiten werden den Framework-Bibliotheken auch deshalb wesentlich weniger verwendet. Schlimmer: Für die Collections-Klassen gibt es mehrere Versionen: Einmal die herkömmliche Version und einmal die generische Version. Wie es sich gehört, auch mit einigen Namensänderungen (z.B. Hashtable vs. Directory).

Normalerweise gilt die Regel, dass man eine API beim ersten Mal richtig machen muss, da es schwierig bis unmöglich ist, eine API für die Code exisiert zu ändern. Bei Generics gibt es aber vielleicht in Java 7 die Chance die generischen Typinformationen zur Laufzeit zu Erhalten ohne bestehenden Code zu brechen. Dies meint zumindest Neal Gafter in "Reified Generics for Java". Eine Übersicht über die Änderungen, die zu Java 7 kommen könnten(!), findet man im Blog "Pure Danger Tech".

Ich glaube, dass der Artikel gut die Ansätze vergleicht und den Vorteil der C#-Version herausarbeitet. Zum Teil geht er vielleicht etwas zu hart mit Java-Generics ins Gericht.