Sunday, April 26, 2009
Some Java Fun becoming a major pain
Author:
Ich hab mal wieder mit GWT rumgespielt und etwas Java programmiert. Da ich ohne map, fold und filter nicht mehr auskomme und Java ja endlich Generics hat, hab ich mir eine kleine Fun Library geschrieben, damit ich z.B. sowas machen kann...
Set<String> strSet = Fun.mapS(intSet, new Fun<String,Integer>() { public Integer eval(String s) { return Integer.parseInt(s); } });
Das funktioniert auch ganz gut, auch wenn die Syntax alles andere als angenehm ist. Ich hab also eine abstrakte Klasse
abstract class Fun<A,B> { public abstract B eval(A x);
mit Hilfsfunktionen wie
public static <A, B> Set<B> mapS(Collection<A> src, Fun<A,B> fun) { // ... }
Wenn man eine Mapping-Funktion mehrfach braucht muss man sie auch nicht immer inline-definierien, sondern kann eine Konstante draus machen, also z.B. im Beispiel oben
Fun<String,Integer> STR_TO_INT = new Fun<String,Integer>() { public Integer eval(String s) { return Integer.parseInt(s); } };
und dann braucht man nur noch
Set<String> strSet = Fun.mapS(intSet, STR_TO_INT)
schreiben. Alles wunderbar... nur dann brauchte ich eine Funktion um aus Map.Entrys die erste Komponente zu extrahieren (map.entrySet() ging in dem Fall nicht). Also
Fun<Map.Entry<String, Integer>, Integer> ENTRYKEY = new Fun<Map.Entry<String, Integer>, Integer>() { public Integer eval(Map.Entry<String,Integer> e) { return e.getKey(); } };
und das funktioniert auch noch gut. Aber dann brauchte ich die Funktion für andere Typparameter. Na, kein Problem, kann man doch sicher generalisieren:
public static <A,B> Fun<Entry<A,B>, A> ENTRY_KEY_1 = new Fun<Entry<A, B>, A>() { @Override public A eval(Entry<A, B> x) { return x.getKey(); }
Nur leider kompiliert das nicht. Polymorphe Werte scheint Java nicht zu unterstützen. (Oder hab ich's nur falsch gemacht?!) Okay, also eine Dummy-Funktion....
public static <A> Fun<Entry<A>, A> ENTRY_KEY_2() { return new Fun<Entry<A>, A>() { @Override public A eval(Entry<A> x) { return x.getKey(); } }; };
Die kann man definieren, aber leider nicht aufrufen!
Pain.java:38: <A>mapS(java.util.Collection<A>,Fun<A>) in Fun cannot be applied to (java.util.Set<java.util.Map.Entry>,Fun<java.util.Map.Entry,java.lang.Object>)
Wahrscheinlich weil nur anhand der Parameter die richtige Belegung ausgewählt werden kann. Also muss man einen Phantom-Parameter einführen...
public static <A> Fun<Entry<A>, A> ENTRY_KEY_3(Entry<A> phantom) { return new Fun<Entry<A>, A>() { @Override public A eval(Entry<A> x) { return x.getKey(); } }; };
und dann beim Aufruf
Set keys = Fun.mapS(map.entrySet(), ENTRY_KEY_3((Entry) null));
Oh.... das tut weh!
Weiss jemand wie das mit Generics in C++ und C# ist? In Scala geht das sicher, oder?
Schade, dass GWT Java SourceCode nach JavaScript kompiliert und nicht, wie ich dachte, Java ByteCode. Damit man mit GWT in den Genuss von Scala kommt muss man wahrscheinlich erst Scala nach ByteCode komilieren und dann mit einem Dekomiler aus dem ByteCode wieder JavaCode machen und den dann mit GWT kompilieren. Oder man hackt gleich getyptes JavaScript in Haskell aber Dmitry Golubovsky ist nicht Google und Version 0.1 ist wohl eher ein Proof of concept. Wahrscheinlich sollte ich halt doch einfach schön imperativ in Java programmieren und Generics nur verwenden um ein paar Casts zu sparen. :-/
Autor: David Leuschner