07.06 Legacy Code

Die Verwendung von älterem Java-Code in neuen Projekten führt oft zu Fehlern bzw. mindestens zu Warnhinweisen. Dabei könnte man die Verwendung solcher „Altlasten“ sicherer gestalten und die Warnungen, welche auftreten, auf ein Minimum reduzieren.

Unchecked Conversion

Am Beispiel des JDOM Paketes schauen wir uns einmal die Portierung von JDK < 1.5 auf ein JDK >=1.5 an. Unsere Methode hierfür verwendet das JDOM Paket, welches vor JDK 1.5 entwickelt wurde. Das Verhalten hat keinen tieferen Sinn, es dient lediglich der Veranschaulichung. Ziel ist die Ausgabe aller Elemente eines Objektes vom Typ org.jdom.Element.

JDOM ist ein Framework, mit welchem man direkt vom Quellcode aus XML Daten einlesen, verarbeiten, manipulieren und ausgeben kann.

Ursprünglich sah eine einfache Ausgabe aller Kindelemente mit zugehörigen Attributen in etwa so aus:

private void printAllElements(Element aStartNode) {

  List elementList = aStartNode.getChildren();
  Iterator elementIter = elementList.iterator();

  while (elementIter.hasNext()) {

    Object obj = elementIter.next();

    if (obj instanceof Element) {

      Element element = (Element) obj;

      System.out.println(element.getName());
      System.out.println(element.getText());

      List attributeList = element.getAttributes();
      Iterator attrIter = attributeList.iterator();

      while (attrIter.hasNext()) {

        Object obj2 = attrIter.next();

        if (obj2 instanceof Attribute) {

          Attribute attribute = (Attribute) obj2;

          System.out.println(attribute.getName());
          System.out.println(attribute.getValue());
        }
      }
      printAllElements(element);
    }
  }
}

Zu erst wird sich eine Liste aller Kinder geholt und diese durchlaufen. Es werden die Elemente der Liste auf Zugehörigkeit zur Klasse org.jdom.Element überprüft und in diesen Typ umgewandelt (cast). Daraufhin erfolgt die Ausgabe des Elementnamens und dessen zugehöriger Text. Anschließend werden alle Attribute dieses Elementes bestimmt und ebenfalls abgearbeitet. Am Ende wird dies rekursiv mit jedem Kindelement wiederholt.

Soweit ist damit alles in Ordnung. Versucht man dies aber mit einem JDK >= 1.5, erhält man zwar einige Warnungen aber man kann es dennoch kompilieren und ausführen. Wieso also diesen Code abändern?

Gründe hierfür:

  • Typsicherheit
  • Kompakter, lesbarer Quellcode
  • Verhinderung von Warnungen
    (dies könnte z.B. Buildprozesse gefährden)

Schauen wir uns diese Warnung einmal an.

  • Iterator is a raw type. References to generic type Iterator should be parameterrized.
  • List is a raw type. References to generic type List should be parameterized.

Uns wird also empfohlen, den untypisierten, “rohen” Typen List näher zu bestimmen, wenn wir den zugehörigen Typen schon wissen. Wir wissen außerdem, dass es seit JDK 1.5 eine verkürzte for-Schleife gibt. Wir könnten also auf den umständlichen Weg über Iteratoren verzichten, wenn wir es schaffen geeignete typisierte Listen zu erzeugen.

In der Quellcode Dokumentation zu Element#getChildren() und Element#getAttributes() steht folgendes:

  • Element#getChildren()
    This returns a List of all the child elements nested directly (one level deep) within this element, as Element objects.
  • Element#getAttributes()
    This returns the complete set of attributes for this element, as a List of Attribute objects in no particular order, or an empty list if there are none.

Wir wissen nun, alle Elemente einer solchen Liste sind vom Typ org.jdom.Element bzw. org.jdom.Attribute.

Dies wollen wir gleich einmal umsetzen.

private void printAllElements(Element aStartNode) {

  List<Element> elementList = aStartNode.getChildren();

  for (Element element : elementList) {

    System.out.println(element.getName());
    System.out.println(element.getText());

    List<Attribute> attributeList = element.getAttributes();

    for (Attribute attribute : attributeList) {

      System.out.println(attribute.getName());
      System.out.println(attribute.getValue());
    }
    printAllElements(element);
  }
}

Mit diesem Wissen konnten wir den Quellcode schon um einiges kürzer fassen. Jetzt erhalten wir aber sogenannte unchecked conversion Warnungen.

  • Type safety: The expression of type List needs unchecked conversion to conform to List<Element>
  • Type safety: The expression of type List needs unchecked conversion to conform to List<Attribute>

Was sind die Ursachen für diese Warnungen?

Die Methoden Element#getChildren() und Element#getAttributes() liefern uns eine untypisierte List zurück, d.h. sie enthält nach außen hin erst einmal nur Elemente vom Typ Object. Wir verlangen aber, dass diese dann in eine Variable vom Typ List<Element> bzw. List<Attribute> verpackt wird. Der Compiler merkt an dieser Stelle also, dass hier eine ungeprüfte, unsichere Konvertierung von Object auf speziellere Objekte vom Typ Element bzw. Attribute vorgenommen wird.

Diese Compiler-Warnungen wurden eingeführt um älteren (legacy) Java-Code weiterhin verwenden zu können, ohne das er durch Compiler-Fehler nicht mehr kompilierbar ist. Der Code des hier verwendeten JDOM Paketes wurde geschrieben, als die Generic-Technik in Java noch nicht verfügbar war.

Wollen wir auch diese letzten Warnungen beseitigen, können wir dies nur noch über Annotations tun. Seit JDK 1.5 gibt es hierfür @SuppressWarnings("unchecked"). Dies muss nun vor unsere Methode mit den unchecked conversion Warnungen.

@SuppressWarnings("unchecked")
private void printAllElements(Element aStartNode) {

...
}

Dies waren ein kurzer Überblick zur Verwendung von älterem Java-Code, den dabei auftretenden Warnungen und deren entsprechende Behandlung.

One Reply to “07.06 Legacy Code”

Schreibe einen Kommentar

Diese Website verwendet Akismet, um Spam zu reduzieren. Erfahre mehr darüber, wie deine Kommentardaten verarbeitet werden.