Generics How-To ?
-
Hi,
beschäftige mich gerade mit generics von Java.
Allerdings bin ich nicht der Java-Guru und greife auf deshalb auf meine Wissen von C++-Templates zurück.Folgendes Codefragment :
class pair<T,U> { private T _first ; private U _second ; /* public pair() { _first=new T() ; // Wie ?? _second=new U() ; } */ public pair(pair<T,U> Value) { _first=Value.getfirst() ; _second=Value.getsecond() ; } public pair(T first,U second) { _first=first ; _second=second ; } public T getfirst() { return _first ; } public U getsecond() { return _second ; } public void setfirst(T first) { _first=first ; } public void setsecond(U second) { _second=second ; } } pair<Integer,Integer> MyValue=new pair<Integer,Integer>(2,3) ; // läßt sich kompilieren pair<int,int> TheValue=new pair<int,int>(2,3) ; // Compile Error unexpected type
Warum läßt sich die Zeile mit "Compile Error" nicht kompilieren.
Wie muss ich den auskommentierten Constructor schreiben damit für _second und _first der DCtor aufgerufen wird.
-
Also erstmal ein Tutorial zu den Generics: http://java.sun.com/j2se/1.5/pdf/generics-tutorial.pdf
Zu 1: Weil man keine built-ins als Typ-Parameter angeben darf.
Zu 2: Verstehe dass Problem nicht. Wenn T und U parameterlose Konstruktoren besitzen, müsste der Code funktionieren.MfG
GPC
-
GPC schrieb:
Zu 2: Verstehe dass Problem nicht. Wenn T und U parameterlose Konstruktoren besitzen, müsste der Code funktionieren.
Nö. Das geht so nicht.
-
Man muss sagen, leider geht dies nicht.
-
grenouille_unreg schrieb:
Man muss sagen, leider geht dies nicht.
Was heisst da leider? Zum Glück geht das nicht! Mit diesem Konstrukt könnte man jede beliebige Klasse instanziieren - auch solche die z.B. Argumente für ihren Konstruktor benötigen, nur private Konstruktoren besitzen, oder gar keine Klassen sondern Interfaces sind... Wers all die möglichen Fehler unbedingt benötigt kann ja Reflection verwenden
-
JBeni schrieb:
Wers all die möglichen Fehler unbedingt benötigt kann ja Reflection verwenden
Das geht möglicherweise nichtmal mit Reflection, weil man dazu ein entsprechendes Class-Object benötigen würde. Wo kriegt man das her? Generics sind über Erasure implementiert. Da gibt es also zur Laufzeit fast keine Informationen über diese Typen mehr. ...aber kann sein, dass man da tricksen kann. Teilweise geht das ja.
Warum kann man eigentlich nicht in einer Art Interface festlegen, dass alle implementierenden Typen einen bestimmten Konstruktor bereitstellen müssen?
-
Ich denke, du denkst an sowas?
class Bla<ConstructorInterface>{ public Bla(){ ConstructorInterface ci = new ConstructorInterface(); } }
Solange man noch folgendes schreiben kann, funktioniert dies nicht:
Bla<ConstrucorInterface> bla = new Bla<ConstructorInterface>();
... denn so müsste ja wieder ein Interface instantiiert werden. Da müsste man noch eine zusätzliche Beschränkung einbauen (nur echte Klassen dürften als generischen Parameter eingesetzt werden).
Zum ursprünglichen Problem: da man, aufrund der Erasure nichts über die Typen weiss, müsste man das "pair" irgendwie informieren. Dann kann man aber auch gleich eine Factory übergeben. Alternativ wäre eine Ableitung von "pair", z.B. "pairStringString" welches 2 Strings beinhaltet, und das auch weiss. Aber mit dieser Lösung kann man auch gleich auf Generics verzichten...
-
Gregor schrieb:
GPC schrieb:
Zu 2: Verstehe dass Problem nicht. Wenn T und U parameterlose Konstruktoren besitzen, müsste der Code funktionieren.
Nö. Das geht so nicht.
Hm, okay, sorry. Schwach. Sehr schwach. Ein weiterer Grund, über die Generics zu lachen und Templates zu preisen :p
-
Und ich dachte immer, Trolle seien die Unregistrierten
P.S. ist sowas mit Templates möglich?
// Eine Klasse class B extends A{} // Eine Liste die irgendwo rumschwebt List<B> listOfB = ... list.add( new B() ); // Eine Methode die nicht auf B's ausgelegt ist void doSomething( List<? extends A> listOfA ){ A a = listOfA.get( 47 ); } // Die Methode für A's mit B's aufrufen, ist typsicher doSomething( listOfB );
-
JBeni schrieb:
Und ich dachte immer, Trolle seien die Unregistrierten
harhar, ich bin viel schlimmer
P.S. ist sowas mit Templates möglich?
Nein. Scheiß Templates, heh ?
-
GPC schrieb:
Gregor schrieb:
GPC schrieb:
Zu 2: Verstehe dass Problem nicht. Wenn T und U parameterlose Konstruktoren besitzen, müsste der Code funktionieren.
Nö. Das geht so nicht.
Hm, okay, sorry. Schwach. Sehr schwach. Ein weiterer Grund, über die Generics zu lachen und Templates zu preisen :p
Über die Generics in Java bitteschön. Wir wollen doch mal festhalten, dass in C# folgendes geht:
public T Foo<T>() where T: new() { return new T(); }
-
Optimizer schrieb:
Über die Generics in Java bitteschön. Wir wollen doch mal festhalten, dass in C# folgendes geht:
public T Foo<T>() where T: new() { return new T(); }
Ähm, sieht etwas schräg aus... was macht das where da hinten? Sagt das aus, dass diese Methode nur für Typen erlaubt ist, die man per DCtor erstellen kann?
-
GPC schrieb:
Ähm, sieht etwas schräg aus... was macht das where da hinten? Sagt das aus, dass diese Methode nur für Typen erlaubt ist, die man per DCtor erstellen kann?
ja. mit where definiert man constraints/concepts - also das interface dass die generics parameter unterstuetzen muessen.
java macht es so: <T extends Foo> wenn ich mich recht erinnere.
in C# geht es ueber where.
where T: Foo, new()das waere nun: muss von Foo erben und das new() constraint unterstuetzen - sprich einfach einen parameterlosen ctor anbieten.
-
Ah ja. Dank dir. Wird in dem Fall zur Laufzeit entschieden, ob's klappt oder nicht?!
-
GPC schrieb:
P.S. ist sowas mit Templates möglich?
Nein. [...]
Hm, okay, sorry. Schwach. Sehr schwach. Ein weiterer Grund, über die Templates zu lachen und Generics zu preisen :p
-
hehe, irgendwie wusste ich, dass du das schreiben würdest
-
GPC schrieb:
Ah ja. Dank dir. Wird in dem Fall zur Laufzeit entschieden, ob's klappt oder nicht?!
Nein, der Compiler kann ohne Probleme jeden Aufruf zur compile-Zeit prüfen.
Test test = Foo<Test>();
Jetzt schaut er nach ob Test einen default Konstruktor hat.
-
Optimizer schrieb:
GPC schrieb:
Ah ja. Dank dir. Wird in dem Fall zur Laufzeit entschieden, ob's klappt oder nicht?!
Nein, der Compiler kann ohne Probleme jeden Aufruf zur compile-Zeit prüfen.
Nimmt er diese Möglichkeit auch wahr?
Weil dann verstehe ich den Sinn der Constraints-Angabe nicht. Wenn wir mal von C++ ausgehen, wo es sowas nicht gibt, und ein T keinen DCtor hat, dann kompiliert das Teil einfach nicht, weil der Compiler halt den DCtor nicht findet. Wozu dann die Constraints-Geschichte?
-
GPC schrieb:
Weil dann verstehe ich den Sinn der Constraints-Angabe nicht. Wenn wir mal von C++ ausgehen, wo es sowas nicht gibt, und ein T keinen DCtor hat, dann kompiliert das Teil einfach nicht, weil der Compiler halt den DCtor nicht findet. Wozu dann die Constraints-Geschichte?
unabhaengig davon ob der c# compiler es wirklich immer zur compile time checken kann, ich weiss es nicht, schonmal eine c++ template fehlermeldung gesehen?
fuer alle leute die es nicht kennen, das ist von stlfilt geklaut:
d:\src\cl\demo>c++2 rtmap.cpp rtmap.cpp: In function `int main()': rtmap.cpp:19: invalid conversion from `int' to ` std::_Rb_tree_node<std::pair<const int, double> >*' rtmap.cpp:19: initializing argument 1 of `std::_Rb_tree_iterator<_Val, _Ref, _Ptr>::_Rb_tree_iterator(std::_Rb_tree_node<_Val>*) [with _Val = std::pair<const int, double>, _Ref = std::pair<const int, double>&, _Ptr = std::pair<const int, double>*]' rtmap.cpp:20: invalid conversion from `int' to ` std::_Rb_tree_node<std::pair<const int, double> >*' rtmap.cpp:20: initializing argument 1 of `std::_Rb_tree_iterator<_Val, _Ref, _Ptr>::_Rb_tree_iterator(std::_Rb_tree_node<_Val>*) [with _Val = std::pair<const int, double>, _Ref = std::pair<const int, double>&, _Ptr = std::pair<const int, double>*]' E:/GCC3/include/c++/3.2/bits/stl_tree.h: In member function `void std::_Rb_tree<_Key, _Val, _KeyOfValue, _Compare, _Alloc>::insert_unique(_II, _II) [with _InputIterator = int, _Key = int, _Val = std::pair<const int, double>, _KeyOfValue = std::_Select1st<std::pair<const int, double> >, _Compare = std::less<int>, _Alloc = std::allocator<std::pair<const int, double> >]': E:/GCC3/include/c++/3.2/bits/stl_map.h:272: instantiated from `void std::map<_ Key, _Tp, _Compare, _Alloc>::insert(_InputIterator, _InputIterator) [with _Input Iterator = int, _Key = int, _Tp = double, _Compare = std::less<int>, _Alloc = st d::allocator<std::pair<const int, double> >]' rtmap.cpp:21: instantiated from here E:/GCC3/include/c++/3.2/bits/stl_tree.h:1161: invalid type argument of `unary * '
wozu koennte man also constraints angeben koennen?
-
Shade Of Mine schrieb:
GPC schrieb:
Weil dann verstehe ich den Sinn der Constraints-Angabe nicht. Wenn wir mal von C++ ausgehen, wo es sowas nicht gibt, und ein T keinen DCtor hat, dann kompiliert das Teil einfach nicht, weil der Compiler halt den DCtor nicht findet. Wozu dann die Constraints-Geschichte?
unabhaengig davon ob der c# compiler es wirklich immer zur compile time checken kann, ich weiss es nicht, schonmal eine c++ template fehlermeldung gesehen?
Sicher, und? Nach nem halben Jahr Übung liest es sich flüssiger wie die BILD.
wozu koennte man also constraints angeben koennen?
Tjo, in dem Fall wohl um den Compiler-Output zu vereinfachen.