Kommentierung


Inhalt:

Projekt-Überblick-Datei
Package-Kommentare
Klassen-Kommentare
Methoden-Kommentare
JavaDoc erzeugen
Kommentare innerhalb Methoden

Allgemein

Any fool can write code that a computer can understand. Good programmers write code that humans can understand.

Real programmers don't comment their code - it was hard to write, it should be hard to understand

Und als Beweis letzterer Aussage: Mein persönlicher Favorit für Variablen-Kommentierung (in C#, Erklärung folgt weiter unten):
    //regulärer Ausdruck für benutzerdefinierte Eingaben
    private Regex neuerReg = new Regex("\\$(?<Tag>[^\\[]*)\\[(?<Typ>[^\\]]*)\\]\\$");


JavaDoc-Kommentare finden zwei Verwendungen:
a) Lesbarkeit des Quellcodes verbessern (z.B. wenn ein neuer Programmierer das Projekt übernimmt)
b) Dokumentation für Closed-Source-Bibliotheken, die ohne Quellcode vertrieben werden, oder auch Open-Source-Bibliotheken, bei denen man den Quellcode nicht direkt zur Hand hat.
In letzterem Fall ist eine gute Dokumentation besonders wichtig.

Die Kommentierung erfolgt auf drei Ebenen:
Gute Kommentare sollten alle Fragen beantworten, die sich bei einen Blick auf Klasse oder Methode stellen.


Projekt-Überblick-Datei

Siehe auch http://docs.oracle.com/javase/7/docs/technotes/tools/windows/javadoc.html#overviewcomment.
Wenn man für die JavaDoc der gesamten Anwendung eine Startseite angeben will, kann man das über einen "overview comment" machen: Man legt eine eigene HTML-Datei ins Projekt (oder an beliebige andere Stelle). Die empfohlene Stelle ist der Quellcodeordner, und der Dateiname könnte "overview.html" sein.
Bei der JavaDoc-Erzeugung wird der "body"-Inhalt dieser Datei in die Datei "overview-summary.html" der generierten JavaDoc übertragen. Zur Verwendung in Eclipse siehe unten.
Laut Oracle-Seite sollte der "title"-Inhalt ebenfalls für die JavaDoc übernommen werden. Aber scheinbar muss man in Eclipse den Titel separat beim JavaDoc-Erzeugen als Option angeben.


Package-Kommentare

Die Package-Kommentare geben einen kurzen Überblick über das jeweilige Package (bzw. in unserem Fall wohl die gesamte Anwendung).
Siehe
http://docs.oracle.com/javase/7/docs/technotes/tools/windows/javadoc.html#packagecomment

Variante 1: "package-info.java"
In jedem package kann eine Datei "package-info.java" liegen. Diese enthält nichts außer einer "package"-Deklaration und JavaDoc-Kommentare dazu:
/**Erklärungen zum Package oder zur Anwendung...
 *  
 * @author Ich war's!
 *
 */
package de.hs_rm.cs.bla.fasel;

Variante 2: "package.html"
Alternativ kann man eine Datei "package.html" ins Package legen. Beim JavaDoc-Erzeugen wird nur der "body"-Inhalt in die JavaDoc übertragen.

Variante 1 (package-info.java) ist wohl die empfohlerene Variante, weil hier Tools (Eclipse) die Datei besser validieren können.

Beispiel für einen Package-Kommentar: http://docs.oracle.com/javase/7/docs/api/java/util/package-summary.html.


Klassen-Kommentare

Die Kommentierung der Klasse sollte einen Überblick über die Funktion der Klasse geben. Nach dem Lesen der Klassenkommentare sollte man wissen, wofür sie überhaupt da ist, und sollte die grundlegende Funktionsweise kennen.
Dies alles gilt natürlich auch für Interfaces ! Falls eine Klasse ein Interface implementiert, sollten die Kommentare an beiden Stellen identisch sein.

Beispiel 1: org.apache.commons.lang.StringUtils aus der Library "commons-lang" der Apache Foundation:
http://commons.apache.org/lang/api-3.1/org/apache/commons/lang3/StringUtils.html.
Es handelt sich um eine Sammlung von static Methoden, die keine Wechselwirkungen untereinander haben. Deshalb hat die Klasse keinen Status (in Form von Membervariablen).
Was finden wir hier an Vorteilen ?

Beispiel 2: java.text.SimpleDateFormat: http://docs.oracle.com/javase/6/docs/api/java/text/SimpleDateFormat.html.
Hier handelt es sich eine Klasse, von der man Instanzen erzeugen muss. Die Klassen-Kommentare gehen leider wenig auf die Verwendung ein.
Was können wir hier lernen ?


Beispiel 3:java.util.Hashtable: http://docs.oracle.com/javase/6/docs/api/java/util/Hashtable.html
Dies ist ebenfalls eine Klasse, von der Instanzen erzeugt werden. Der Klassenkommentar gibt einen groben Überblick über die Verwendung und geht detailliert auf Performanz-Fragen ein.
Was bringt uns das ?


Gegenbeispiel 4: aus dem JBoss 4.0.5: http://docs.jboss.org/jbossas/javadoc/4.0.5/system/org/jboss/deployment/MainDeployer.html
Bei dieser schicken Klasse besteht der Kommentar aus "The main/primary component for deployment."
Es steht nichts dazu, was sie überhaupt macht oder wie man Instanzen erzeugt (das ist vor allem knifflig, wenn eine Client-Anwendung von außerhalb des Servers auf sie zugreift).

Zusammenfassung:
Ein Klassenkommentar sollte:


Methoden-Kommentare

Die nächste Stufe der Kommentierung nach den Klassenkommentare sind die Methoden-Kommentare.
Die Kommentare müssen für die jeweilige Methode beschreiben, was genau sie tut, und welche Rahmenbedingungen zu beachten sind.

Beispiel 1: aus dem Stateless-Beispiel:
  /**
   * Berechnet ein Quader-Volumen
   * 
   * @param a Seite a
   * @param b Seite b
   * @param c Seite c
   * @return Volumen
   * @exception InvalidParameterException
   */
  public double computeCuboidVolume(double a, double b, double c) throws InvalidParameterException
Die grundlegende Funktionalität ist beschrieben.
Nicht so gelungen sind hier:


Beispiel 2: das gleiche in besser:
  /**
   * Berechnet ein Quader-Volumen nach der Formel "a*b*c"
   * 
   * @param a Seite a, mit Wert >= 0
   * @param b Seite b, mit Wert >= 0
   * @param c Seite c, mit Wert >= 0
   * @return Volumen nach der Formel "V = a * b * c"
   * @exception InvalidParameterException Wenn a oder b oder c < 0
   */
  public double computeCuboidVolume(double a, double b, double c) throws InvalidParameterException

Hier sind die obigen Punkte behoben, die Doku läßt hoffentlich keine Fragen offen.

Beispiel 3: org.apache.commons.lang.exception.ExceptionUtils aus dem Apache-Commons-Paket:
http://commons.apache.org/lang/api-3.1/org/apache/commons/lang3/exception/ExceptionUtils.html#getMessage(java.lang.Throwable)
Der Kommentar ist nicht allzu ausführlich, aber ich denke man hat nach einem Blick darauf ein brauchbare Vorstellung vom Verhalten bekommen. Auch hier:

Beispiel 4: Besonderheiten bei Verwendung:
Dieser Ausschnitt ist aus dem KuchenZutatNM-Beispiel. Ich gebe zu, dass er in dieser Form dort nicht so zu finden ist ;-).
  /**Abrufen der Zutatenliste. 
   * 
   * Beim Entfernen einer Zutat aus dem Kuchen werden die verbundenen Zutaten NICHT gelöscht !
   * Beim Laden des Kuchen werden sie NICHT mitgeladen. Ein Verwender kann die Zutatenliste eines aus der DB geladenen Kuchen also
   * nur abrufen, solange die Bean unter Entity-Manager-Kontrolle ist.
   *  
   * @return Liste der Zutaten.
   */
  @ManyToMany(mappedBy="kuchen", cascade={CascadeType.MERGE, CascadeType.PERSIST, CascadeType.REFRESH}, fetch=FetchType.LAZY)
  public Collection<ZutatNMBean> getZutaten()
  {
    return this.collZutaten;
  }
Da die Annotations nicht im JavaDoc erkennbar sind, muss hier einiges zur Verwendung erklärt werden, nämlich der Umgang mit den Zutaten beim Löschen des Kuchens, sowie die Auswirkungen des FetchType.LAZY.


Beispiel 5: Verweise:
Jetzt wollen wir Beispiel 4 noch verbessern: um auch im JavaDoc-HTML die beiden Seiten der Relationship zu "verbinden", können wir unter Verwendung des @see-Tags Verweise zu anderen Methoden oder Klassen erzeugen. Das Javadoc-Tool erzeugt dabei HTML-Links, durch die man mit einem Klick zur anderen Methode gelangt.

  /**Abrufen der Zutatenliste. 
   * 
   * Beim Entfernen einer Zutat aus dem Kuchen werden die verbundenen Zutaten NICHT gelöscht !
   * Beim Laden des Kuchen werden sie NICHT mitgeladen. Ein Verwender kann die Zutatenliste eines aus der DB geladenen Kuchen also
   * nur abrufen, solange die Bean unter Entity-Manager-Kontrolle ist.
   *  
   * @return Liste der Zutaten.
   * @see ZutatNMBean#getKuchen()
   */
  @ManyToMany(mappedBy="kuchen", cascade={CascadeType.MERGE, CascadeType.PERSIST, CascadeType.REFRESH}, fetch=FetchType.LAZY)
  public Collection<ZutatNMBean> getZutaten()
  {
    return this.collZutaten;
  }
Die Syntax ist: "Package.Klasse#Methode". Die Angabe des Packages ist optional, wenn wir das Ziel des Verweises innerhalb des gleichen Packages wie die aktuelle Klasse liegt.


Gegenbeispiel 6: viele offene Fragen
Dieses Beispiel ist aus einer .NET-Library, mit der ich in der Firma arbeiten muss, und die wir angekauft haben. Die Doku einer Enumeration für Excel-Linienstile sieht so aus:
XLLineStyleEnum
Die Kommentare zu den einzelnen Werten geben keine Informationen, die über den Enum-Namen hinausgehen. Interessant wäre z.B. gewesen wieviele Pixel eine "Medium"-Linie breit ist.


Gegenbeispiel 7: großes Mysterium
Dieser Codeausschnitt stammt aus einem C#-Projekt, ich bin bei mir auf der Arbeit darüber gestolpert. Es ist eine Property in C#-Syntax, aber hoffentlich verständlich. Man beachte die Kombination von nichtssagenden Namen (was ist ein Primär-/Sekundär-Intervall?) und belanglosen Kommentaren:
            /// <summary>
            /// Setzt und liefert den Primaer-Intervall
            /// </summary>
            public DateTimeNullableVonBis PrimaerIntervall
            {
                  get
                  {
                        return this.intervallPrimaer;
                  }
                  set
                  {
                        this.intervallPrimaer = value;
                  }
            }

            /// <summary>
            /// Setzt und liefert den Sekundär-Intervall
            /// </summary>
            public DateTimeNullableVonBis SekundaerIntervall
            {
                  get
                  {
                        return this.intervallSekundaer;
                  }
                  set
                  {
                        this.intervallSekundaer = value;
                  }
            }


Gegenbeispiel 8: ich weiß doch, was das macht!
Dieses Methode stammt aus der Realität und dient dazu, einen RGB-Wert in einen Grauton umzurechnen.
    /// <summary>
    /// Macht aus einer Farbe einen Grauton.
    /// </summary>
    /// <param name="farbe"></param>
    /// <returns></returns>
    private Color ProjeziereAufGrau(Color farbe)
    {
      int intGrau = (int) Math.Round( 255* Math.Sqrt(farbe.R* farbe.R + farbe.G*farbe.G + farbe.B * farbe.B) / (Math.Sqrt(3) * 255) ,0);
      Color graustufe = Color.FromArgb(intGrau, intGrau, intGrau);
	  return graustufe;
    }
Erfunden hat sie ein ehemaliger Mathematiklehrer, für den geometrische Operationen wohl ein Kinderspiel sind. Aber ich hätte keine Chance, das Verfahren nachzuvollziehen.


Trivial-Operationen:
Man mag argumentieren, dass Kommentare für Trivial-Operationen wie Property-Zugriffe unnötige Tipparbeit sind. Meiner Meinung nach bedeutet ein nicht vorhandener Kommentar allerdings nur, dass man absolut keine Aussage über die Methode machen kann.
Dass die Methode "KuchenBean.getName()" nur die Property "name" zurückliefert, ist für den Programmierer, der nur die JavaDoc zur Hand hat, nicht zu erkennen. Für ihn ist die Methode eine Blackbox, und hier kann alles passieren. Wer garantiert, dass "getName" nicht einen aufwändigen Datenbankzugriff ausführt, um den Namen des aktuellen Kuchens aus einer völlig anderen Datenbank-Tabelle zu holen ? Der Programmierer weiß allerdings, dass ihm kein Unheil droht, wenn im Kommentar etwas harmloses wie "Liefert den Namen des Kuchens" steht, und nichts davon steht dass hier schlimmer Voodoo vonstatten geht.

Ein weiteres Beispiel: "ZutatBean.getMenge/setMenge": Was hat man sich unter "Menge" vorzustellen? Welchen Regeln genügt dies ? Dass hier ein Freitext ohne weitere Regeln dahintersteckt, und Eingaben wie "100g Mehl", "5 Liter Milch" etc. erlaubt sind, sieht man von außen nicht. Eigentlich hilft auch ein Blick in den Quellcode nicht weiter, denn dies ist eine Information, die man nur aus der Anforderungsdefinition erhält, und die existiert nur in meinem Kopf.


Doppel-Kommentare in Interface und Klasse:
In der EJB-Welt sind Local-/Remote-Interface so dicht verdrahtet, dass die Kommentare für Interface und Bean-Klasse eigentlich absolut identisch sein können. Rein theoretisch würde es also genügen, z.B. das Interface zu dokumentieren, und in der implementierenden Methode der Bean-Klasse nur mittels @see-Tag auf die Interface-Methode zu verweisen. Allerdings macht es grundsätzlich Sinn, bei der Klassen-Methode einige Details über die Implementierung zu beschreiben, falls diese Relevanz für den Verwender haben.
Beispielsweise genügt für eine allgemeine Interface-Methode "getDaten" die Beschreibung "Liest die Daten ein, wobei dies abhängig von der Implementierung ist". Die implementierenden Klassen könnten jetzt aber Beschreibungen wie "Liest die Daten aus der Datenbank" oder "Greift auf eine XML-Datei zu um die Daten einzulesen. Diese muss folgendes Format haben: ...".

Für den Fall, dass Kommentare in Klassenmethode und Interfacemethode absolut identisch sind, kann man folgenden Kommentar deklarieren:
  /**{@inheritDoc}
   */
  @Override
  public KuchenNMBean findKuchenById(Integer int_Id)
  {
Das Tag {@inheritDoc} sucht den Methodenkommentar in Basisklassen oder Interfaces und übernimmt ihn von dort. Allerdings muss man die Kommentare trotzdem in Remote- und Local-Interface duplizieren.
Siehe: http://docs.oracle.com/javase/6/docs/technotes/tools/windows/javadoc.html#inheritingcomments

Das Tag gilt per Default für den gesamten Kommentar. Es lässt sich aber auch für einzelne Parameter, den Rückgabewert oder Exceptions definieren, so dass nur deren Kommentare übernommen werden.
Übrigens kann man den aus der Interface-Methode gezogenen Kommentar auch erweitern.
  /**{@inheritDoc}
     Zusätzlicher Kommentar.
     @param int_Id {@inheritDoc} (und außerdem gilt für diesen Parameter auch: ...)
   */
  @Override
  public KuchenNMBean findKuchenById(Integer int_Id)
  {
Jetzt wird der Methodenkommentar aus dem Interface gezogen und mit dem zusätzlichen Kommentartext verbunden. Das gleiche gilt für den Parameter-Kommentar. Die Kommentare aus dem Interface werden jeweils an der Stelle des {@inheritDoc} eingetragen.



Zusammenfassung:
Ein Methodenkommentar sollte:

JavaDoc erzeugen

Das Erzeugen von JavaDoc geschieht leider in Eclipse nicht manuell. Man wählt das Projekt aus, für das man JavaDocs erzeugen will, und ruft im Menü "File" -> "Export..." auf. Im Export-Dialog wählt man "Java" -> "Javadoc" aus:
Export Javadoc
Alternativ wäre auch Menü "Project" => "Generate Javadoc..." möglich und würde zum selben nächsten Schritt führen.

Im nächsten Schritt wählt man die Projekt aus, für die man generieren will. Ich empfehle hier, alle Teilprojekte der Enterprise-Application zu wählen, im Beispiel also EJB-, Web- und ApplicationClient-Projekt. Außerdem wird die Option "Create Javadoc for members with Visibility" auf "Protected" gesetzt. Als Zielpfad empfehle ich ein Unterverzeichnis außerhalb des Projekts. Grund: ansonsten explodiert unsere Fehlerliste, weil das generierte HTML ebenfalls validiert wird.

Im Feld "javadoc command" muss man eventuell erst den Pfad zu "javadoc.exe" eintragen, je nachdem ob man im Projekt eine JRE oder ein JDK verwendet.
Export Javadoc
In Schritt 3 können wir alles auf den Defaults lassen.
In Schritt 4 müssen wir die "JRE source compatibility" vom Default "1.3" auf "1.6" ändern, damit Annotations verarbeitet werden:
Export Javadoc

Falls wir eine Anwendungsüberblick-Datei erzeugt haben ("overview.html)", geben wir den Pfad zur HTML-Datei in Schritt 4 an (Screenshot ist noch aus einer veralteten Version der Dokumentation - der Dateiname "package-template.html" stammt aus einem Mißverständnis meinerseits!):
Export Javadoc (Package-Kommentar)

Die Ausgabe des Javadoc-Laufs erscheint auf einer Konsole, und hier sollten wir ein Auge auf Fehler oder Warnungen haben.


Kommentare innerhalb Methoden

Diese Kommentare spielen für JavaDoc bzw. für Verwender der Library natürlich keine Rolle. Sie sind aber wichtig, wenn ein neuer Entwickler ins Projekt kommt, oder wenn man selbst Workarounds einbaut und sich später noch an diese erinnern will.
Ich würde empfehlen, logisch zusammenhängende Codeblöcke per Kommentar zusammenzufassen. Also nicht vor jeder Codezeile ein Kommentar, sondern immer vor Teilstücken.

Beispiel 1:
Dieses Beispiel stammt aus der "KuchenZutatNMWorkerBean" des KuchenZutatNM-Beispiel:
  /**Die übergebene Zutat löschen.
   * 
   * @param intZutatId ID der zu löschenden Zutat.
   * @exception EntityNotFoundException Wenn Zutat nicht gefunden wurde.
   */
  public void deleteZutat (Integer intZutatId)
  {
    logger.info ("deleteZutat " + intZutatId);
	
    //Die Zutat im EntityManager laden.
    //Hier mit "getReference" arbeiten, damit eine böse EntityNotFoundException fliegt wenn Zutat nicht gefunden wird.
    ZutatNMBean zutat = this.entityManager.getReference(ZutatNMBean.class, intZutatId );
   
    //Jetzt wird es knifflig: wir müssen die Kuchen der Zutat holen,
    //und aus den Zutat-Collections der Kuchen die Zutat entfernen.
    //Grund scheint zu sein dass der EntityManager ansonsten noch die
    //Zutat im Kuchen hält und dabei in einen Fehlerzustand läuft.
    Collection<KuchenNMBean> listeKuchen = zutat.getKuchen();
    Iterator<KuchenNMBean> iteratorKuchen = listeKuchen.iterator();
    while (iteratorKuchen.hasNext() == true) 
    {
      //Zutat aus der Zutatenliste des Kuchens entfernen:
      KuchenNMBean kuchenMitZutat = iteratorKuchen.next();
      kuchenMitZutat.getZutaten().remove(zutat);
    }

    //Jetzt endlich dürfen wir die Zutat löschen.
    this.entityManager.remove(zutat);
  }
Hier sind einzelne Abschnitte kommentiert. Es gibt zwei wichtige Fragen:

Beispiel 2:
Dieses Beispiel stammt aus der "KuchenZutatNMWorkerBean" des KuchenZutatNM-Beispiel:
  /**Finden des Kuchens zur übergebenen ID.
   * 
   * @param int_Id ID des gesuchten Kuchens
   * @return Gefundener Kuchen oder null, wenn nicht gefunden (es gibt in diesem Fall keine Exception)!
   */
  public KuchenNMBean findKuchenById(Integer int_Id)
  {
    logger.info ("findKuchenById " + int_Id);
    
    //Den Kuchen im EntityManager laden.
    //Hier mit "find" arbeiten, da bei ungültiger ID "null" zurückkommen soll. 
    KuchenNMBean kuchen = this.entityManager.find(KuchenNMBean.class, int_Id);
    
    //Falls etwas gefunden wurde, dann die Relationship einlesen.
    if (kuchen != null)
    {
      //Da der FetchType der ManyToMany-Relation auf LAZY gesetzt ist müssen die Zutaten 
      //hier explizit abgerufen werden solange die KuchenNMBean noch nicht
      //detached ist. Später würde ansonsten ein Zugriff auf die Zutatenliste eine
      //Exception auslösen.
      Collection<ZutatNMBean> collZutaten = kuchen.getZutaten();
      //Wir müssen einen Zugriff auf die Collection selbst ausführen, Abrufen der Property alleine reicht nicht !
      logger.info ("Anzahl Zutaten: " + collZutaten.size());
    }
    else
    {
      logger.info ("Kuchen zur ID " + int_Id + " nicht gefunden !");
    }
    
    return kuchen;
  }  
Der Methoden-Kommentar macht präzise Aussagen, was bei einer ungültigen ID passiert.

Innerhalb der Methode ist der relevante Kommentar der Workaround für Probleme beim FetchType.LAZY: Schaut ein Entwickler später in den Code, dann wird er wahrscheinlich keinen Sinn mehr hinter dem doch scheinbar unnötigen collZutaten.size() erkennen. Deshalb muss hier ein Kommentar hin.

Gegenbeispiel 3: Selbsterklärend!
Dieses Beispiel (C#) findet sich in diesem Kapitel, da es sich um eine private Membervariable handelt, die deshalb in generierter Javadoc nie auftaucht und deshalb nicht für das Erstellen von Libraries relevant ist.
    //regulärer Ausdruck für benutzerdefinierte Eingaben
    private Regex neuerReg = new Regex("\\$(?<Tag>[^\\[]*)\\[(?<Typ>[^\\]]*)\\]\\$");
Zur Erklärung mein Wissensstand zu dem Zeitpunkt, als ich das entsprechende Feature nutzen wollte und irgendwo im Quellcode die Doku dazu suchte:
Es handelt sich um eine Klasse "Abfrage", über die im Programm SQL-Statements unter einem bestimmten Namen abgespeichert werden können. Später können User diese Abfragen aus einer Liste auswählen und ausführen und erhalten das Resultset der Datenbankabfrage als Tabelle zurück. Ich wusste außerdem, dass man in diesen Abfragen irgendwie Parameter angegeben kann (die vom User per Dialog abgefragt werden), also z.B. ein Datum. Für diese Parameter muss ein Anzeigename und ein Datentyp angegeben werden. Und auf der Suche nach der genauen Syntax habe ich an der untersten Stelle angefangen, also bei der Deklaration der regulären Expression.

Preisfrage: wer kann die Syntax hierfür skizzieren bzw. ein Beispiel bauen (eine Auflösung gibt es nicht ;-) )?

Man beachte auch den geschickt und aussagekräftig gewählten Namen der Variablen ;-).
Ich habe später an einer völlig anderen Stelle im Code (nämlich in der Klasse für den Bearbeiten-Dialog der Abfragen) den gesuchten Kommentar zumindest in Ansätzen gefunden, aber da war es schon zu spät.

Falls ihr später mal eurem Chef Rechenschaft ablegen müsst: ich brauchte ca. 30 Minuten, bis ich über Trial&Error die richtige Syntax herausgebracht hatte. Wie lange hätte eine aussagekräftige Kommentierung gedauert?

Gegenbeispiel 4
Folgenden Kommentar eines Kollegen habe ich am 26.11.2007 in einem Codeschnipsel gefunden (die von ihm geänderte Zeile führte nebenbei zu einem Crash an anderer Stelle). Es handelt sich übrigens um eine C#-Property:
    public override DateTime Von
    {
      get
      {
        ...
      }
      set
      {
        //Anpassen auf den NÄCHSTEN Montag:
        DateTime datMontag = DatumZeitFunktionen.GetMontag(value);
        // Korrektur JK 18.01.2007
        if (datMontag < value) datMontag = datMontag.AddDays(7);
        base.Von = datMontag;
      }
Wir haben danach eine Viertelstunde lang gerätselt, was die eingefügte Zeile zu bedeuten habe, und mussten am Ende sogar beim Kunden anrufen, um uns bestätigen zu lassen, was wir entschlüsselt hatten ;-). Ich muss meinem Kollegen allerdings zugute halten, dass er immerhin ein "Schuldeingeständnis" in den Code gesetzt hat (in Form eines Änderungs-Kommentars). Ansonsten hätte ich die mir unsinnig erscheinende Zeile wohl einfach auskommentiert (versehen mit einem "//Erscheint mir sinnlos und führt zu Bug in Situation xyz (WKnauf 26.11.2007)"), und dadurch wäre die Situation wohl nicht besser geworden ;-).

Gegenbeispiel 5
Nochmal ein Beispiel aus der praktischen Arbeit: im folgenden Codeschnipsel waren zwei Zeilen auskommentiert.
Der originale Code erschien mir sinnvoll, aber es war nicht zu erkennen, a) wer der "Schuldige" war und b) warum es überhaupt auskommentiert war.
            if (PeMKBBDataGlobal.HasBenutzerRecht(AppBaseGUIGlobal.ApplicationBase, AppBaseGUIGlobal.ApplicationBase.Benutzer, RechtPeMKBB.Objekt_Termin_Zeiten_Bearbeiten))
            {
              //this.dtpIstBeginn.Enabled = true;
              //this.dtpIstEnde.Enabled = true;
              this.dtpBeginn.Enabled = true;
              this.dtpEnde.Enabled = true;
              this.pnlBeginnEndeSynch.ReadOnly = false;
            }


Zusammenfassung
Kommentare der Form "Schleife über xyz" oder "IF-Abfrage", nur um meinen Kommentar-Hunger zu stillen, sind natürlich total unnötig. Wenn es wirklich nichts zu sagen gibt, dann ist auch kein Kommentar nötig. Aber sobald eine IF-Abfrage mehrere mit UND oder ODER verknüpfte Bestandteile hat, muss man die Bedingung einfach in Prosa-Text übersetzen, sonst kapiert man sie selbst nicht. Meine Trivial-Beispiele enthalten leider nicht allzuviele Stellen, an denen es wirklich knackig wird. Aber die Realität besteht oft aus fünfzeiligen IF-Bedingungen mit vielen Klammern ;-).


Stand 03.01.2013
Historie:
16.12.2008: erstellt aus Vorjahresbeispiel, angepaßt an Eclipse 3.4, mehr schlechte Beispiel aus der Praxis.
31.12.2008: RegEx-Beispiel
03.06.2009: {@inheritDoc} erweitert
12.07.2009: Negativ-Beispiel 8 für Methodenkommentare
21.12.2011: Div. Links repariert
03.01.2013: Overview-Datei, Beschreibung der Package-Kommentare korrekt gemacht und erweitert