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.
Monday, November 26, 2007
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 wahrWä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.
Friday, October 26, 2007
Bad News: Kein Java 6 in Leopard
Von Blueâs Blog gestiftete Leopard-Konsolenausgabe:
Java(TM) 2 Runtime Environment, Standard Edition (build 1.5.0_13-b05-237)
Java HotSpot(TM) Client VM (build 1.5.0_13-119, mixed mode, sharing)
Apple hat keine neue Java-Version zusammen mit Leopard veröffentlicht.
Naja, irgendwie war damit zu rechnen, weil Java 6 schon nicht in der 300 Einträe umfassenden Neuerungenliste von Leopard aufgetaucht war, aber enttäuschend ist es schon. Java 6 ist nun nicht brandneu (schon über ein Jahr) und bald werden wohl auch die ersten Projekt auftauchen, die Java 6 only sind (z.B. evtl. Glassfish 3).
Wednesday, October 24, 2007
Googlemail bekommt IMAP
GMail/Googlemail integiert (langsam) IMAP Zugriff auf die Mailpostfächer.
Bisher hat GMail nur POP3 Zugang unterstützt. Die zusätzliche IMAP-Funktion ist wirklich ein großer Schritt nach vorne, wenn es darum geht GMail mit Mail.app oder anderen E-Mail-Clients zu verwenden.
Statusänderungen wie Lesen und Löschen* werden automatisch mit dem Online-Postfach synchronisiert.
Die neue Funktion wird offensichtlich nur langsam unter den Benutzer ausgerollt. In meinen Einstellungen erscheint immer noch nur "POP3 Download". Aber die Supportdokumente sind online und so wird die Funktion wohl früher oder später jeden erreichen.
via Downloadsquard
* Leider nicht als Archivieren verstanden, sondern als Verschieben in einen Ordner "Deleted Items".
Friday, October 19, 2007
Meine Wintersemester-Planung
Es ist eine gute Tradition hier an dieser Stelle die Vorlesungsplanung für das nächste Semester vorzustellen.*
In diesem Jahr ist es unklarer als sonst, weil ich nicht mehr so viel Zeit (noch 2 Semester) habe die Vorlesungen zu hören.
Gesetzt:
- "Operating Systems" bei Prof. Rammig
- "Rechnernetze (Seminar)" bei Prof. Karl
- "Projektgruppe Orcos" bei Prof. Rammig
Noch unklar (1, 2 oder 3 werden überleben)
- Philosophie der Technik (Studium Generale)
- Leistungsbewertung und Simulation
- Maschinelles Lernen
- Web-Engineering
- Verteiltes Problemlösen
[*] Die ebenso gute Tradition der VKrit ist wegen technischer Probleme (iBook!) doch leicht in Verzug. Ich versuche aber die Texte über die Vorlesungen des letzten Semesters noch zu schreiben.
Saturday, October 13, 2007
Wo bleibt .NET auf dirkmeister.de?
Es gab gestern eine "Beschwerde", dass ich an dieser Stelle zu viel über Java schreibe und kaum über .NET, auch wenn ich mit C#-Entwicklung (zumindest in den letzten zwei Jahren) einen nicht unerheblichen Teil meines Lebensunterhaltes bestritten habe.
Eine Kritik, die wohl nicht ganz unberechtigt ist.
Ich weiß nicht ob dies nach gestern noch zu ändern ist, aber ich kann ja damit beginnen die Webseiten und Blogs zum Thema C#/.NET zu nennen, die ich im Feedreader abonniert habe:
- Ganz vorne zu nennen wäre hier: CodeBetter.com. Ein Gemeinschaftsblog von 16 .NET Entwicklers, wovon 9 MVPs sind. Ein Focus ist dort im Moment ALT.NET mit alternativen .NET-Frameworks wie NHibernate, MonoRails, etc.
- Peter Brombergâs UnBlog ist das .NET Blog von (offensichtlich) Peter Bromberg, ebenfalls ein MVP. Dort schreibt er über alle möglichen Themen aus dem .NET Umfeld und insbesondere neue Entwicklungen und Technologien.
- Quasi das Lokal-Blog: OutOfCoffeeException ist das Blog des UPB-Informatikstudenten und Microsoft Student Partner Matthias Raacke. Manchmal erscheint es mir etwas zu werbemäßig. Deshalb mit etwas Vorsicht zu geneißen.
- Die Blogs der Commerce Server Entwickler David Messner, Ryan R. Donovan und Vinayak Tadas haben mir bei der Entwicklung mit dem Microsoft Commerce Serer (2k3, 2k7) wirklich weitergeholfen. Die Dokumentation des Commerce Servers ist definitiv suboptimal. Tipps und Tricks über die MSDN-Doku hinaus, gibt es dort!
- Interessant finde ich auch den Ansatz von Adam Esterline nach C# Puzzlers (1, 2) ähnlich zu den Java Puzzlers zu suchen. Allerdings glaube ich nicht, dass die direkte Übersetzung von "Java Puzzler" in "C# Puzzler" erfolgversprechend ist. C# wird ganz eigene Cornercases haben.
Saturday, October 06, 2007
Double != double
In Java existieren von je her für alle primitiven Typen wie int, double entsprechende Wrapperklassen. Für int ist dies die Klasse java.lang.Integer. double hat java.lang.Double als Gegenstück.
Die Verwendung von Wertdatentypen in Sammlungsklassen wie ArrayList oder HashMap war immer etwas beschwerlich. Die Collections-Klassen können nämlich nur mit echten Objekten umgehen. Wie man an dem nachfolgenden Beispiel sieht, muß man beim Einfügen und Herausholen eine manuelle Konvertierung - Boxing - vom primitiven Typ in das Wrapperobjekt durchführen:
int i = 99; //value to add
List list = new ArrayList();
list.add(Integer.valueOf(i));
[...]
int j = list.get(0).intValue();
Mit Java 1.5 wurde neben den Generics (oben schon gesehen) auch das so genannte Auto-Boxing eingeführt. Damit wird, wenn notwendig, automatisch eine Konvertierung zwischen primitiven Typen und den entsprechenden Wrapperklassen vorgenommen. Das gleiche Beispiel sieht unter Java 1.5 so aus:
int i = 99;
List list = new ArrayList();
list.add(i);
[...]
int j = list.get(0);
Der Code ist offensichtlich wesentlich aufgeräumter, aber Auto-Boxing ist primär "Syntactic Sugar", weil die notwendigen Methodenaufrufe (und Casts) immer noch ausgeführt werden.
Sie müssen nur nicht mehr explizit niedergeschrieben werden.
Auch in vielen anderen Fällen kann man mit den Objekten auf den ersten Blick so rechnen wie mit den Werten. Dies ermöglicht zum Beispiel diesen (realen) Code:
public final Double getDuration() {
if (duration == 0.0) {
duration = syncDuration + headerLength * secPerByte;
duration += enclosedPacket.getDuration();
}
return duration;
}
Konzeptionell sähe die Methode bei der Ausführung so aus:
public final Double getDuration() {
if (duration == 0.0) {
duration = syncDuration + headerLength * secPerByte;
duration += enclosedPacket.getDuration().doubleValue();
}
return Double.valueOf(duration);
}
Dieses Beispiel zeigt aber auch warum man stets zwischen Double und double unterscheiden sollte, auch wenn es so aussieht, als ob man den Unterschied weitgehend ignorieren könnte.
Performance
Ein Profiling der Anwendung, aus der dieses Codestück stammt, hat ergeben, dass die Anwendung 38% der Zeit in der Methode Double.valueOf verbringt und 14% der Zeit in Double.doubleValue.
Die Aufrufe sind nicht direkt erfolgt, sondern diese Methoden werden "unsichtbar" durch das Autoboxing erzeugt. [*]
Die Performance der Anwendung wurde halbiert auf Grund des Unterschiedes zwischen Double und double - Ein Buchstabe, Große Wirkung!
Nur weil man die Methodenaufrufe im Vergleich zu Java 1.4 nicht mehr sieht, sind sie trotzdem nicht weg. Der Unterschied zwischen Objektdatentyp und Werttyp ist nicht aufgehoben, sondern nur die Konvertierung ist einfacher. Die Methoden Double.valueOf und Double.doubleValue sind absolut triviale Methoden, aber es kann sich aufsummieren. In diesem Fall auf 50% der gesamtes Laufzeit. [**]
NullPointerExceptions
NullPointerExceptions (NPE) sind schon in normalen Fällen eine böse Sache, weil sie keinen wirklichen Hinweis darauf geben, was die wahre Ursache für den Fehler ist. Oftmals liegt der Fehler, der zu einer NullPointerException führt, an einer ganz anderen Stelle im Code als der Ursprung der Exception. Nicht umsonst liest man oft den Rat Nullen gar nicht erst in den Code zu lassen.
Noch schlimmer sind NullPointerExceptions, die durch Autoboxing entstehen.
Was passiert bei folgenden Code:
Integer obj;
[a lot of lines]
int i = obj * 10;
Richtig. Es wird eine NullPointerException geworfen, da obj nicht auf eine Referenz gesezt wurde. Wäre obj vom Typ int, so wäre es perfekt legaler Code bei dem i = 0 wäre.
Jeder Versuch ein auf null gesetztes Nummernobjekt automatisch zu konvertieren, endet in einer NPE.
Während es bei normalen NPE noch den Dereferenzierungsoperator "." als Hinweis gibt, sind Autoboxing-NPE quasi unsichtbar.
Mit Nummernobjekten ist es auch aufwändiger über die Korrektheit zu argumentieren. Der mögliche Zustandsraum ist um diesen elenden null-Fall größer als bei Werttypen. Man muß stets überlegen, ob das Objekt an der Codestelle null sein kann und wie darauf reagiert werden muß z.B. durch eine extra Prüfung auf != null oder eine inhaltlich sinnvolle Exception wie IllegalArgumentException.
Die Möglichkeit eine Variable auf null zu setzen fügt dem Zustandsraum eine zusätzliche Äquivalenzklasse hinzu, über die man extra nachdenken und die man i.d.R. mit eigenen Testfällen testen muß.
Beachtet man diesen wichtigen Unterschied nicht, so führt dies schnell zu ziemlich fragilen, unrobusten Programmen.
Vergleich zu C#
C# hat hier das Glück des Zweitgeborenen. Es konnte aus den Ungereimtheiten von Java lernen und dort wurde die Problematik gelöst, in dem int, long, etc. weitgehend normale Objekte sind und so auch in Collections eingefügt werden können. Der wichtigste Unterschied zu normalen Objekten ist, dass man die Wertobjekte nicht auf Null setzen kann. Mit .NET 2.0 wurde diese Möglichkeit durch eine Spracherweiterung (int?, double?, ...) nachgeliefert.
double ist dort nur ein Alias für den Frameworktyp System.Double. Vielleicht ist dies auch der Grund für den performance-hungrigen Code oben. Unter C# würde es dort kein Problem geben. Möglicherweise war der Entwickler eher in der C#-Welt zu Hause.
Fazit
Die Nummbernobjekte und Auto-Boxing sollten primär in Zusammenhang mit den Collections-Klassen oder ähnlichen Anwendungsfällen verwendet werden.
Im Zweifel kann man die Nummernobjekte auch verwendet, wenn man ausdrückten muß, dass ein Wert nicht gesetzt wurde (null).
Oftmals ist dies auch keine so gute Idee, aber es ist doch auf jeden Fall besser als Double.NaN und normalen Vergleichoperating für diesen Zweck einzusetzen (wie in der gleichen Anwendung an anderer Stelle gesehen).
Für alle anderen Fälle insbesondere in Berechnungen sollte man eher die Werttypen verwenden.
Die Dokumentationsseite der Java-API sagt dazu auch:
So when should you use autoboxing and unboxing? Use them only when there is an "impedance mismatch" between reference types and primitives, for example, when you have to put numerical values into a collection. It is not appropriate to use autoboxing and unboxing for scientific computing, or other performance-sensitive numerical code.
An Integer is not a substitute for an int; autoboxing and unboxing blur the distinction between primitive types and reference types, but they do not eliminate it.
1) Es gab noch ein paar Nummernobjekte in der Anwendung (, die bis auf eine Ausnahme alle besser Werte gewesen wären. 2) Ich bin der erste der zugeben würde, dass die Duration-Berechnung auch unabhängig von der Verwendung des Double-Objektes unglücklich entworfen und implementiert ist 3) Es ist nicht allein dieser Codestück für das Profilresultat verantwortlich, aber es war das zentrale Problem.
* Dieses Beispiel ist in meinen Augen auch ein Musterbeispiel für den Optimierungsgrundregel, dass man - wenn möglich - auf Basis von Messungen optimieren sollte. Diese kleine unscheinbare Methode war das absolute Performancebottleneck. Nur durch "scharfes Hinsehen" hätte man dies niemals herausfinden können.
Entire Berkeley Lectures on YouTube, More to Come
[Why I write English articles once in a while] The famous "University of California, Berkeley" makes some of its entire lectures freely available on YouTube.
The press release states that
UC Berkeley is the first university to make videos of full courses available through YouTube. Visitors to the site at youtube.com/ucberkeley can view more than 300 hours of videotaped courses and events. Topics range from bioengineering, to peace and conflict studies, to "Physics for Future Presidents," the title of a popular campus course. Building on its initial offerings, UC Berkeley will continue to expand the catalog of videos available on YouTube.Unfortunately, up to now there aren't any Computer Science courses are available on YouTube. But the old webcast site of Berkeley doesn't seem to be dead. There you can stream videos of lectures using the RealPlayer (since 2001!). It would be great if Berkeley would replace the streaming with YouTube since the RealPlayer streaming is very unrelyable even with 4k DSL. Even better than YouTube would be a "real" video podcast, but I assume that would cause to much traffic.
One of the lecures the university is offering on the old webcast site is "Operating systems and system programming", the Berkeley analog to "Konzepte und Methoden von Systemsoftware". It is nice to see how Paderborn (or better Prof. Karl) performs in a (more or less) direct comparision to John Kubiatowcz, one of the fathers of the P2P systems OceanStore and Tapestry.
Since I haven't it done before, it is also worth to mention the iTunesU program where a lot of american universites post recordings of some of their lectures.
[via Jendrik Bertram]
Thursday, October 04, 2007
Microsoft veröffentlicht Quellcode des .NET-Framework
Einer der größten Hindernisse beim Entwickeln mit dem .NET-Framework war stets, dass der Quellcode nicht verfügbar gewesen ist. Das hat .NET stets von Java oder auch der MFC unterschieden.
In 99,9% der Zeit reichen die Interfacebeschreibungen und die Dokumentation in der MSDN durchaus aus, aber die Doku ist nicht wirklich überall gleich gut (um nicht zu sagen, an einigen Stellen kaum vorhanden). In diesen restlichen 0,1% der Zeit wird .NET dann einfach zu einer riesigen Blackbox.
Dieses Hindernis wird aber nun aus dem Weg geräumt.
Der Microsoft-Mitarbeiter Schott Guthrie hat in seinem Blog angekündigt, dass der Quellcode wichtiger .Net-Bibliotheken veröffentlicht wird.
Mit .NET 3.5, dass noch in diesem Jahr erscheint, wird der Quellcode (inkl. Kommentaren) von vielen Basisklassen, ASP.NET, Windows Forms, ADO.NET, XML und WPF verfügbar gemacht.
Man wird den Quellcode entweder seperat oder über eine Integration mit Visual Studio 2008 herunterladen können .
Die Lizenz des Codes wird nur nur ein Lesen des Quellcodes erlauben. Änderungen und Weitergabe sind untersagt. Aber schon das Lesen und die Möglichkeit zum Debuggen wird eine große Hilfe sein.
[via CodeBetter.com]
Thursday, August 09, 2007
Buchtipp: Java Puzzlers
In "Java Puzzlers - Traps, Pitfalls, and Corner Cases" stellen Joshua Bloch und Neal Gafter, zwei herausragende Personen der Java-Welt, 95 "Java Puzzle" vor. Dies sind kleine Codestücke, bei denen der Leser herausfinden soll, was der Code macht. In der Regel tut er nicht, das was von ihm erwartet wird.
Anschließend stellen die Autoren stets vor, was der Code wirklich macht und gibt Tipps, wie man die Probleme vermeiden kann. Die Puzzle drehen sich um verschiedene Bereiche von Java u.a. die Probleme von automatischen Casts, Generics, Autoboxing (beliebte Quelle für NullPointerExceptions), Klasseninitialisierung und Exceptionhandling. Aber auch die Java-Bibliothken werden angesprochen.
Die Puzzle und die Auslösungen sind wirklich extrem verzwickt und wären ideal für einen sadistischen GP1-Tutor. Das Lesen hat einfach Spaß gemacht.
In einem zwei Monate altem Artikel von mir habe ich vermutet, dass der Gebrauchswert von "Java Puzzler" geringer ist als der des Buches "Effective Java".
Dem ist wohl auch so, aber "Java Puzzlers" ist nicht nur nett zu lesen, sondern kann tatsächlich eine hilfreiche Lektüre sein.
In einem aktuellen Universitätsprojekt ist man gleich in mehreren Fällen auf die im Buch präsentierten Fallstricke "reingefallen",z.B. auf Puzzle 1 (Oddity), Puzzle 29 (Bride of Looper), Puzzle 57 (Whatâs in a Name?) und Puzzle 58 (Making a Hash of It).
Als Fazit kann man sagen, dass es nicht den gleichen praktischen Wert hat wie "Effective Java", aber mit dem Tipps und dem Wissen aus dem Buch wird man nicht so leicht über die Randbereiche von Java stolpern bzw. man wird eher erkennen, wo die Randbereiche liegen. Nun kann man leicht sagen, dass in gut geschriebener Software die aller meisten Probleme aus den Puzzles nicht auftauchen. Die meisten Puzzles sind schon ziemlich obskur, so dass die meisten Entwickler den Code sowieso umschreiben würden. Aber die Hinweise sind auch in der Realität wichtig (siehe oben) auch weil nicht jeder Code perfekt geschrieben ist. Wenn man Code vorgesetzt bekommt, der nicht gut geschrieben ist und weitgehend ungetestet, dann können die Hinweise Gold wert sein.
Die gute Nachricht ist, dass viele der Randfälle aus dem Buch auch durch das statische Analysetool FindBugs (eigentlich mal einen eigenen Artikel wert) gefunden werden. Um ehrlich zu sein, habe ich die oben erwähnten Bugs nicht durch Java Puzzlers sondern durch FindBugs entdeckt.
Wer in Java Puzzler hineinlesen möchte, kann die Probekapitel auf javapuzzlers.com lesen. So interessant wie das Buch sind auch die jährlichen Puzzlevorträe auf der JavaOne-Konferenz, die online ansehen gewerden können. Bei Sun gibt es die Talks als Audiopräsentation mit Transskript und den Slides (2007, 2006) , den 2007-Vorträ gibt es auch bei Google-Video (Joshua Block zusammen mit FindBugs-Autor Bill Pugh)
Fazit: Kein Muss-Lesestoff, aber durchaus interessant, lehrreich und gleichzeitig Fun. Zumindest wenn man nicht absolut allergisch auf die Bezeichnung "Geek" reagiert.