In Java template-spezialisierung simulieren?
-
Hi,
in der Hoffnung, dass hier Leute mitlesen, die sowohl in java als auch C++ programmieren, habe ich hier mal eine Frage wie ich c++-Code nach Java portiere.
Folgende Situation:
C++:template <typename T> class Foo { public: void bar(); private: T _baz; }; template<> Foo<int>::bar() { _baz = getIntFromSomewhere(); } template<> Foo<double>::bar() { _baz = getDoubleFromSomewhere(); }
Jetzt möchte ich diesen Mechanismus in Java haben. Ich weiß, dass Java Generics beschränkter Bockmist und nichts anderes als syntactic sugar sind, daher zielt meine Frage eher dahin, wie das krude Konstrukt aussieht, dass mir dieses Verhalten simuliert:
Java:
class Foo<T> { private T _baz; public void bar() { _baz = ??? } };
-
Ich würde Java Generics nicht als Bockmist bezeichnen, sind auch einige Sachen möglich, die in C++ nicht möglich sind.
package javaapplication3; /** * * @author Zeus */ public class Main { public static Integer GetInteger() { return 12; } public static Double GetDouble() { return 1.1213141; } public class Foo<T> { public void bar() { } protected T baz; } public class IntegerFoo extends Foo<Integer> { @Override public void bar() { baz = GetInteger(); } } public class DoubleFoo extends Foo<Double> { @Override public void bar() { baz = GetDouble(); } } /** * @param args the command line arguments */ public static void main(String[] args) { } }
-
Zeus schrieb:
Ich würde Java Generics nicht als Bockmist bezeichnen, sind auch einige Sachen möglich, die in C++ nicht möglich sind.
Okay, wollte jetzt keinen flamewar starten
...
Das sieht doch schon sehr gut aus. Ich dachte, ich müsste Unterscheidung zur Laufzeit (mit instanceof) machen, aber das sieht schon deutlich besser aus.
EDIT: Nochwas:
Was ist das equivalent zu std::numeric_limits<float>::epsilon?
Float.MIN_VALUE ??
-
Ohh mann, ich glaube, ich muss doch auf den Flamewar zurück kommen. Meine Methodensignaturen gehen nicht mehr, weil sie in Java ununterscheidbar sind:
public void requestData(long id, Bar<Integer> ref) { } public void requestData(long id, Bar<Float> ref { }
Error:Method requestData(long, Bar<Float>) has the same erasure requestData(long, Bar<T>) as another method in type ...
Jetzt muss ich entweder für jeden Dreckstypen eine leere Alias-Klasse nach obigen Vorbild anlegen, oder den Methoden Dummy-Parameter geben, damit sie unterscheidbar sind. Gott, was ein Bockmist...
-
Wieso musst eine Unterscheidung getroffen werden? In diesen Beispiel haben Bar<Integer> aka Foo<Integer>? und Bar<Float> aka Foo<Float> die Schnistellen von Bar<T>.
-
Weil das eine Art Factory für Foo ist.
Foo != Bar !!!Also ich versuch es mal:
interface Bar<T> { public void update(T value); }
interface IFoo { void blah(); }
class Foo<T> implements IFoo { }
Wie oben
class IntegerFoo extends Foo<Integer> { }
Wie oben.
Dann gibts eine Art Factory, die zu einem gegebenen Bar<T> Objekt das passende Foo<T> bzw TFoo Objekt bauen soll und es in einer Map von id, IFoo speichert:
class Example { public void requestData(long id, Bar<Integer> ref) { map.put(makeId(id), new IntegerFoo(ref); } public void requestData(long id, Bar<Float> ref { map.put(makeId(id), new FloatFoo(ref); } }
Ich hoffe, ich habe jetzt die Struktur verständlich wiedergegeben. Also: Die korrekt typisierte Foo-Unterklasse muss instantiiert werden anhand des Types des Bar-Parameters. In C++ gar kein Problem. In Java mit der damischen Type-erasure schon.
Trotzdem schonmal danke für den Input bis hierher.
Noch eine Idee zu std::numeric_limits ??
-
Ja leider ist Java für derartige Sachen nicht zu gebrauchen, weil die Generics nicht im Bytecode vorhanden sind. In C# geht sowas.
public <T extends Integer> void run(T t) {} public <T extends Float> void run(T t) {}
X<T> wird immer auf X<Object> abgebildet und ein anderes X<T> auch :S.
So gehts:
public class Bar<T> { } public class IntegerBar extends Bar<Integer> { } public class FloatBar extends Bar<Float> { } public class Dispatcher { public <T extends IntegerBar> void requestData(long id, T ref) { } public <T extends FloatBar> void requestData(long id, T ref) { } }
Aber die generischen Methodenparameter kann man sich dann auch Sparen.
-
Zeus schrieb:
So gehts:
public class Bar<T> { } public class IntegerBar extends Bar<Integer> { } public class FloatBar extends Bar<Float> { } public class Dispatcher { public <T extends IntegerBar> void requestData(long id, T ref) { } public <T extends FloatBar> void requestData(long id, T ref) { } }
Aber die generischen Methodenparameter kann man sich dann auch Sparen.
Schon klar, aber der Methodenparameter ist ja nicht T, sondern Bar<T>. Sieht aber ansonsten sehr gut aus.