Saturday, September 17, 2011

Project Lombok

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.

Saturday, September 10, 2011

Java => Scala: Scalava

So many are lamenting about how crappy Java is and that we need a replacement (like me). But do we? Shouldn't we just start by using the tools which java gives in the most awesome way possible?

Ingredients needed:
Java 6
guava.jar (google-collections)
lombok.jar (add to classpath and IDE)

Features:

Packages (included in java)
Originally, they seem to be designed to contain "modules", while public was only intended for those (perhaps few) ankle points where they were communicating with the "outer" world. That idea has a lot (and i mean a lot) of problems (like interfaces and their demand for public methods), so it does not make sense to really track that route consistently. But it still does make sense to use packages to ease your development:
Advantages:
- No visibility modifier needed (good for methods)
- multiple toplevel-classes in a single file (good for implementing many small classes)
- Allows you to make certain "dangerous" stuff available in a broader sense than private but still restricted to a fixed amount of classes you can control - unlike protected and public.

Unnamed Fields, classes and Methods
Scala uses conventions to make methods invisible and provide singletons.
By using a trick, you an do this in java as well: Just create elements which consist of nothing more than an underscore. Sounds strange? It is, in fact just a convention with nice effects:

package functions: class _ {}
Create a PACKAGE-PRIVATE _ in each of your packages to store static functions which do not belong to objects. That way, you have a place where all your fresh functions can end up, until they provide usefull enough to get their own "utilsXYZ".
In the end, no instantiable class should have static methods beside factories, and this is the way to enforce this and help to re-use code!
Same goes for constants
Note that the class must be package-private: otherwise, you end up with WAY to many _ in your code-completion!

Singletons:
Singletons can be stored in a public _ field, which reduces their code bloat and makes the name nicely readable: Singleton._ instead of Singleton.get() or Singleton.INSTANCE crap.

Singleton-Factories
Useful trick for interfaces:
public interface List{
   ListFactory _ = new ListFactory();
   class ListFactory{
      LIst of(){ ... }
   }}

Usage:
List._.of()