da der Scala-Weg derzeit immer noch zu kontrovers diskutiert wird, habe ich mir Projekt Lombok angesehen. Projekt Lombok pimpt die Sprache Scala um Bytecode-Generatoren und ein neues Sprachfeature. Es arbeitet mit Annotations und einen Java-Agent, der mit javac, eclipsec und gwtc funktioniert.
Projekt Lombok stellt vor allem drei wichtige Features für die Java-Entwicklung bereit:
@Data-Annotation
Äquivalent zu einer Scala-Caseclass. Erzeugt automatisch Konstruktor für finale Felder, getter,setter, equals/hashcode und toString.
class Person{
final String name;
final int age;
}
Vorteile:
- Weniger Code -> Bessere Übersicht, und keine Bugs (falsche Felder im Konstruktor gesetzt, equals/hashcode nicht auf neue Felder abgestimmt etc)
- Da reine Datenstrukturen leichter erstellbar sind, werden sie auch häufiger genutzt werden. Pusht funktionale Programmierung in Java, da Daten und Funktion leichter trennbar sind
- Final-Felder ohne Konstruktor-Overhead
- Besondere Getter und Setter die mehr als nur ihren Job machen, werden die leicht erkennbar
- Alle Methoden können immer noch "manuell" definiert werden!
val-Keyword
Godlike: Bei finalen lokale Variablen kann statt des Typs einfach val geschrieben werden, wie in Scala:
val s = new ArrayList<String>();
final ArrayList<String> s = new ArrayList<String>();
Technisch gesehen ist val ein syntethischer typ aus org.lombok, den der java-agent bei der Compilation gegen den richtigen Typ austauscht.
Vorteile:
- Bevorzugt massiv die Verwendung finaler Variablen, was gut ist
- Pusht Generics, die ebenfalls gut sind
- Variablen, die wirklich veränderlich sind jetzt leichter erkennbar und werden mit Typangaben "bestraft".
Das ganze funktioniert nur bei lokalen Variablen, nicht bei Parametern und Feldern. Ist in Scala aber oft nicht anders.
Drittens gibt es noch die Delegate-Annotation, die dann hilft wenn eine Klasse zwei Interfaces "mixen" soll. Ermöglicht teilweise ähnliche Funktionen wie Mixins in Scala. Das Feature ist sehr gut für spätere Refactoring-Jobs geeignet:
public class StringList extends AbstractList<Character> implements CharSequence{
@Delegate
final String data;
public StringList(String s) { data = s; }
public Character get(int i){ return data.charAt(i); }
pulblic int size() { return data.length(); }
}
Obiges Konstrukt erzeugt einen Mixin aus List<Character> und CharSequence. Die Schnittstellen dafür bietet ja bereits durch seine Interface-Vererbung an. Die Delegates ermöglichen es nun aber auch, die Implementierungen zu vererben - besser: weiterzuleiten:
StringList enthält jetzt alle von AbstractList geerbten Methoden UND alle Methoden von String.
Je nach Wunsch hätte man auch die Delegation einschränken können, indem man nur die Methoden des Interfaces CharSequence delegiert.
Local Typeinference, Case-Classes, "Mixins": Gute Zeiten für anspruchsvolle Java-Entwicklung. :-)
Stabilität von Lombok
Eine offene Frage mag die nach der Stabilität bzw. Portierbarkeit sein. Die Project-Page gibt dazu schon detaillierte Infos, ich kann aber noch meine Erfahrungen beisteuern (eclipse-only):
data: extrem stabil
val: stabil, man sollte es aber nicht an den falschen Stellen (Methoden-Parameter, Klassenfelder) einbauen. Dann gibts schon mal eclipse-Fehlermeldungen (AST-Exceptions). Diese haben aber keine weiteren Auswirkungen.
delegate: nicht stabil. Das Compilieren klappt ohne Probleme, der eclipse-Editor kommt damit aber noch nicht so gut zurecht. Auch hier kommen öfters AST-Exception Fehlermeldungen ohne weitere Auswirkungen. Da es eh das am selten genutzteste Feature ist, ist das aber nicht so schlimm, da es nur für im Editor offene Klassen gilt.
Exit-Strategie?
Was wenn man es nicht mehr einsetzen will? Was wenn man ein besonderes Environment hat in dem keine Java-Agents greifen? In dem Fall hat Lombok gegenüber der Wahl einer Alternativsprache einen Riesenvorteil: Das Projekteigene Tool kann aus mit Lombok-Annotationen versehenen Klassen direkt die entsprechenden Gegenstücke in "reinem" Javasourcecode erzeugen.
Wenn sich dass Tool also mitten im Projekt als fehlerhaft oder falsche Wahl erweist dann ist der Wechsel trivial: Delombok auf die Quellen aufrufen und man kann so mit dem Erzeugen Quellcode direkt weiterarbeiten.
Projekt Lombok stellt vor allem drei wichtige Features für die Java-Entwicklung bereit:
@Data-Annotation
Äquivalent zu einer Scala-Caseclass. Erzeugt automatisch Konstruktor für finale Felder, getter,setter, equals/hashcode und toString.
class Person{
final String name;
final int age;
}
Vorteile:
- Weniger Code -> Bessere Übersicht, und keine Bugs (falsche Felder im Konstruktor gesetzt, equals/hashcode nicht auf neue Felder abgestimmt etc)
- Da reine Datenstrukturen leichter erstellbar sind, werden sie auch häufiger genutzt werden. Pusht funktionale Programmierung in Java, da Daten und Funktion leichter trennbar sind
- Final-Felder ohne Konstruktor-Overhead
- Besondere Getter und Setter die mehr als nur ihren Job machen, werden die leicht erkennbar
- Alle Methoden können immer noch "manuell" definiert werden!
val-Keyword
Godlike: Bei finalen lokale Variablen kann statt des Typs einfach val geschrieben werden, wie in Scala:
val s = new ArrayList<String>();
final ArrayList<String> s = new ArrayList<String>();
Technisch gesehen ist val ein syntethischer typ aus org.lombok, den der java-agent bei der Compilation gegen den richtigen Typ austauscht.
Vorteile:
- Bevorzugt massiv die Verwendung finaler Variablen, was gut ist
- Pusht Generics, die ebenfalls gut sind
- Variablen, die wirklich veränderlich sind jetzt leichter erkennbar und werden mit Typangaben "bestraft".
Das ganze funktioniert nur bei lokalen Variablen, nicht bei Parametern und Feldern. Ist in Scala aber oft nicht anders.
Drittens gibt es noch die Delegate-Annotation, die dann hilft wenn eine Klasse zwei Interfaces "mixen" soll. Ermöglicht teilweise ähnliche Funktionen wie Mixins in Scala. Das Feature ist sehr gut für spätere Refactoring-Jobs geeignet:
public class StringList extends AbstractList<Character> implements CharSequence{
@Delegate
final String data;
public StringList(String s) { data = s; }
public Character get(int i){ return data.charAt(i); }
pulblic int size() { return data.length(); }
}
Obiges Konstrukt erzeugt einen Mixin aus List<Character> und CharSequence. Die Schnittstellen dafür bietet ja bereits durch seine Interface-Vererbung an. Die Delegates ermöglichen es nun aber auch, die Implementierungen zu vererben - besser: weiterzuleiten:
StringList enthält jetzt alle von AbstractList geerbten Methoden UND alle Methoden von String.
Je nach Wunsch hätte man auch die Delegation einschränken können, indem man nur die Methoden des Interfaces CharSequence delegiert.
Local Typeinference, Case-Classes, "Mixins": Gute Zeiten für anspruchsvolle Java-Entwicklung. :-)
Stabilität von Lombok
Eine offene Frage mag die nach der Stabilität bzw. Portierbarkeit sein. Die Project-Page gibt dazu schon detaillierte Infos, ich kann aber noch meine Erfahrungen beisteuern (eclipse-only):
data: extrem stabil
val: stabil, man sollte es aber nicht an den falschen Stellen (Methoden-Parameter, Klassenfelder) einbauen. Dann gibts schon mal eclipse-Fehlermeldungen (AST-Exceptions). Diese haben aber keine weiteren Auswirkungen.
delegate: nicht stabil. Das Compilieren klappt ohne Probleme, der eclipse-Editor kommt damit aber noch nicht so gut zurecht. Auch hier kommen öfters AST-Exception Fehlermeldungen ohne weitere Auswirkungen. Da es eh das am selten genutzteste Feature ist, ist das aber nicht so schlimm, da es nur für im Editor offene Klassen gilt.
Exit-Strategie?
Was wenn man es nicht mehr einsetzen will? Was wenn man ein besonderes Environment hat in dem keine Java-Agents greifen? In dem Fall hat Lombok gegenüber der Wahl einer Alternativsprache einen Riesenvorteil: Das Projekteigene Tool kann aus mit Lombok-Annotationen versehenen Klassen direkt die entsprechenden Gegenstücke in "reinem" Javasourcecode erzeugen.
Wenn sich dass Tool also mitten im Projekt als fehlerhaft oder falsche Wahl erweist dann ist der Wechsel trivial: Delombok auf die Quellen aufrufen und man kann so mit dem Erzeugen Quellcode direkt weiterarbeiten.