Logisch und detailliert – Eigene Logical Structures und Detail Formatter


de KaffeeKlatsch Eclipse java

Erschienen im KaffeeKlatsch, Juli 2018 (07/2018)


Debugging könnte so einfach sein, wenn der Debugger immer genau die gewünschten Information in der gerade jetzt besten Form anzeigen würde. Automatisches “Do What I Mean” (DWIM) ist zwar immer noch nicht möglich, aber mit “Detail Formatter” und “Logical Views” kommt der Eclipse-Debugger dem Programmierer ein ganzes Stück entgegen.

Normalerweise zeigt der Debugger von Eclipse in der “Variables”-View oder beim “Inspect”-Befehl die Objekte und deren Felder als Baum. Zusätzlich dazu zeigt er auch das Ergebnis von toString() an. Die Darstellung als Baum ist aber bei komplexen Datenstrukturen nicht aussagekräftig. Die innere Struktur einer LinkedList oder einer LinkedHashMap ist nur für den Implementierer dieser Datenstruktur wichtig, allerdings nicht für den Anwender. Daher kann man für jede Klasse eine oder mehrere logische Darstellungen (“Logical Structure”) einstellen, die für die jeweilige Situation besser passen. Eclipse bringt für einige Standardtypen wie Collection, Map und Map.Entry schon Definitionen mit, die diese Datenstrukturen als flache Arrays bzw. Key/Value-Paar anzeigen (siehe Abbildung 1).

Abbildung 1

Vor Eclipse Oxygen war diese Darstellung übrigens nicht der Standard. Man musste in der Icon-Leiste der “Variables”-View (und ähnlichen Views) die Option “Show Logical Structure” aktivieren.

Abbildung 1 zeigt aber auch, dass die logische Struktur eines Objektes manchmal nicht ausreicht, wenn z.B. die toString()-Methode der Listenelemente nicht oder unpassend implementiert ist und so das Auffinden in der Liste erschwert. Für dieses Problem bietet Eclipse “Detail Formatter” an.

Mit den Standardeinstellungen zeigt Eclipse die Ausgaben eines Detail Formatters “nur” in der “Detail Pane” – also z.B. im unteren Teil der “Variables”-View an. In den Preferences kann man aber unter “Java → Debug → Detail Formatters” im Kasten “Show variable details” (siehe Abbildung 2) einstellen, dass dieser Wert auch in der Baumstruktur als “Label” (d.h. in der rechten Spalte) erscheint (siehe Abbildung 3 in den “key”-Zeilen). Mit dieser Einstellung und aktivierter “Show Logical Structure”-Option kann man in Hash-Maps deutlich einfacher bestimmte Einträge finden.

Abbildung 2
Abbildung 3

Eigene Detail Formatter und eigene Logical Structures werden auf ähnliche Weise über die Debug-Preferences definiert. In beiden Fällen benötigt man neben einem Namen und einem Typ, auf den sich der Detail Formatter oder die Logical Structure bezieht, noch mindestens einen Code-Schnipsel. In Abbildung 2 ist ein Ausdruck für folgende einfache Klasse definiert:

public class MyKey {
	private final String partOne;
	private final String partTwo;

	public MyKey(String partOne, String partTwo) {
		this.partOne = partOne;
		this.partTwo = partTwo;
	}

	// Kein toString()
}

Der Ausdruck im Detail Formatter oder der Logical View wird von Eclipse so interpretiert, als ob er direkt in der betreffenden Klasse definiert wird – man hat also direkten Zugriff auf die privaten Felder. Ein sehr einfacher Ausdruck für den Detail Formatter ist also:

partOne+"."+partTwo

Liefert der Ausdruck keinen String zurück, so wird an dem Ergebnis automatisch toString() aufgerufen.

Eclipse erlaubt – nicht ganz so offensichtlich – sowohl im Detail Formatter als auch in der Logical Structure nicht nur einen Ausdruck, sondern auch mehrere Anweisungen. Der Detail Formatter kann also auch so implementiert werden:

StringBuilder sb = new StringBuilder();
sb.append(partOne);
sb.append('.');
sb.append(partTwo);
return sb;

Eclipse erlaubt hier also, eine beliebig komplexe Logik zu formulieren – nur Klassendefinitionen, neue Methoden und – leider – Lambda-Ausdrücke sind nicht möglich. Außerdem gibt es auch keine import-Anweisungen. Alle Typen müssen also vollständig qualifiziert angegeben werden.

Will man z.B. wissen, ob in einer großen, unübersichtlichen Map Values der Form bla6, bla7 oder bla8 vorkommen, kann man für die Map folgenden Detail Formatter definieren:

Map m =  new LinkedHashMap();
for(Entry e : entrySet()) {
  if( e.getValue().toString()
        .matches("bla[678]"))
      m.put(e.getKey(), e.getValue());
}
return m;

Dadurch erscheinen in der Detail-Pane nur die ausgewählten Einträge.

Ein komfortablerer Filter lässt sich aber mit einer eigenen Logical Structure Definition einrichten.

Eclipse unterstützt für einen Typ nur einen Detail Formatter, aber beliebig viele Logical Structure Definitionen. Die gerade aktive Logical Structure wird über das Kontext-Menü an einem Objekt in der Baum-Ansicht im Untermenü “Show Logical Structure” ausgewählt. Dadurch kann man schnell zwischen einer ungefilterten und einer gefilterten Ansicht umschalten.

Es gibt zwei Varianten von Logical Structures: Bei der “Single Value” Variante gibt man nur einen Code-Schnipsel an – das Ergebnis wird dann in der Objektbaum-Ansicht dargestellt. Gibt man hier Collections oder Arrays zurück, stellt der Debugger sie als aufklappbaren Baum dar. Eclipse liefert z.B. für Map eine Single Value Logical Structure mit folgendem Code aus:

return entrySet().toArray();

Die zweite Variante von Logical Structures, “List of Values” genannt, erlaubt beliebig viele zusätzliche Unterstrukturen zu definieren. Für jede Unterstruktur gibt man einen eigenen Namen und einen eigenen Code-Schnipsel an. Eine erweiterte Logical Structure für Map könnte also die Werte size, keySet, values und entrySet definieren, die in der “Variables”-View getrennt auf- und zugeklappt werden können (Abbildung 4 und 5).

Abbildung 4
Abbildung 5

Ebenso wie bei einem Detail Formatter erlaubt Eclipse bei Logical Structures nicht nur einfache Ausdrücke, sondern mehrere Statements pro Wert. Da außerdem mehrere unterschiedliche Logical Structures pro Typ möglich sind, können hier noch einfacher Filter vordefiniert und umgeschaltet werden. Zum Beispiel kann die “extended Map”-Definition um den zusätzlichen Wert bla678 mit folgendem Code-Schnipsel erweitert werden:

List l =  new ArrayList();
for(Entry e : entrySet()) {
  if( e.getValue().toString()
        .matches("bla[678]"))
     l.add(e);
}
return l.toArray();

Allerdings zeigt die “Variables”-View mit diesem Code nicht die gewünschte Teilmenge der Map an, sondern den Fehler Type mismatch: cannot convert from element type Object to Map.Entry. Der Fehler tritt nicht mehr auf, wenn die Logical Structure für java.util.HashMap statt für java.util.Map definiert wird.

Derartige Hakeleien sind ärgerlich, machen aber deutlich, dass es sich um Debugging-Hilfen und keine vollwertigen Programme oder Plugins handelt. Dennoch sind Detail Formatter und Logical Structures wichtige Werkzeuge zur Untersuchung komplexer Datenstrukturen wie z.B. die Multimaps von Guava, Hibernate Sessions und Ähnlichem.

Kurzbiografie

Andreas Heiduk ist als Senior Consultant für MATHEMA Software GmbH tätig. Seine Themenschwerpunkte umfassen Domänenspezifische Sprachen, die Java Standard Edition (JSE) und die Java Enterprise Edition (JEE). Daneben findet er die unterschiedlichsten Themen von hardwarenaher Programmierung bis hin zu verteilten Anwendungen interessant.