Antworten:
Es gibt einen großen Unterschied zwischen ihnen. In C ++ müssen Sie keine Klasse oder Schnittstelle für den generischen Typ angeben. Aus diesem Grund können Sie wirklich generische Funktionen und Klassen mit der Einschränkung einer lockeren Eingabe erstellen.
template <typename T> T sum(T a, T b) { return a + b; }
Die obige Methode fügt zwei Objekte desselben Typs hinzu und kann für jeden Typ T verwendet werden, für den der Operator "+" verfügbar ist.
In Java müssen Sie einen Typ angeben, wenn Sie Methoden für die übergebenen Objekte aufrufen möchten, z.
<T extends Something> T sum(T a, T b) { return a.add ( b ); }
In C ++ können generische Funktionen / Klassen nur in Headern definiert werden, da der Compiler verschiedene Funktionen für verschiedene Typen generiert (mit denen er aufgerufen wird). Die Kompilierung ist also langsamer. In Java hat die Kompilierung keine großen Nachteile, aber Java verwendet eine Technik namens "Löschen", bei der der generische Typ zur Laufzeit gelöscht wird, sodass Java zur Laufzeit tatsächlich ...
Something sum(Something a, Something b) { return a.add ( b ); }
Generische Programmierung in Java ist also nicht wirklich nützlich, es ist nur ein wenig syntaktischer Zucker, um mit dem neuen foreach-Konstrukt zu helfen.
EDIT: Die obige Meinung zur Nützlichkeit wurde von einem jüngeren Ich geschrieben. Die Generika von Java helfen natürlich bei der Typensicherheit.
extends
oder angeben super
. Antwort ist falsch,
Java Generics sind massiv unterschiedlich zu C ++ Vorlagen.
Grundsätzlich handelt es sich bei C ++ - Vorlagen im Grunde genommen um einen verherrlichten Präprozessor- / Makrosatz ( Hinweis: Da einige Leute eine Analogie nicht verstehen können, sage ich nicht, dass die Vorlagenverarbeitung ein Makro ist). In Java handelt es sich im Grunde genommen um syntaktischen Zucker, um das Boilerplate-Casting von Objekten zu minimieren. Hier ist eine ziemlich anständige Einführung in C ++ - Vorlagen im Vergleich zu Java-Generika .
Um auf diesen Punkt näher einzugehen: Wenn Sie eine C ++ - Vorlage verwenden, erstellen Sie im Grunde eine weitere Kopie des Codes, als ob Sie ein #define
Makro verwendet hätten. Auf diese Weise können Sie beispielsweise int
Parameter in Vorlagendefinitionen festlegen, die die Größe von Arrays usw. bestimmen.
Java funktioniert so nicht. In Java erstrecken sich alle Objekte von java.lang.Object . Vor Generics würden Sie also Code wie folgt schreiben:
public class PhoneNumbers {
private Map phoneNumbers = new HashMap();
public String getPhoneNumber(String name) {
return (String)phoneNumbers.get(name);
}
...
}
weil alle Java-Sammlungstypen Object als Basistyp verwendeten, sodass Sie alles in sie einfügen konnten. Java 5 rollt herum und fügt Generika hinzu, damit Sie Dinge tun können wie:
public class PhoneNumbers {
private Map<String, String> phoneNumbers = new HashMap<String, String>();
public String getPhoneNumber(String name) {
return phoneNumbers.get(name);
}
...
}
Und das ist alles, was Java Generics sind: Wrapper zum Casting von Objekten. Das liegt daran, dass Java Generics nicht verfeinert werden. Sie verwenden Typlöschung. Diese Entscheidung wurde getroffen, weil Java Generics so spät in den Artikel kam, dass sie die Abwärtskompatibilität nicht aufheben wollten (a Map<String, String>
ist immer dann verwendbar, wenn a benötigt Map
wird). Vergleichen Sie dies mit .Net / C #, wo keine Typlöschung verwendet wird, was zu allen möglichen Unterschieden führt (z. B. können Sie primitive Typen verwenden IEnumerable
und IEnumerable<T>
haben keine Beziehung zueinander).
Eine Klasse mit Generika, die mit einem Java 5+ -Compiler kompiliert wurden, kann unter JDK 1.4 verwendet werden (vorausgesetzt, sie verwendet keine anderen Funktionen oder Klassen, für die Java 5+ erforderlich ist).
Deshalb werden Java-Generika als syntaktischer Zucker bezeichnet .
Diese Entscheidung über die Erstellung von Generika hat jedoch so tiefgreifende Auswirkungen, dass die (hervorragenden) häufig gestellten Fragen zu Java-Generika aufgetaucht sind, um die vielen, vielen Fragen zu beantworten, die Menschen zu Java-Generika haben.
C ++ - Vorlagen bieten eine Reihe von Funktionen, die Java Generics nicht bietet:
Verwendung primitiver Typargumente.
Beispielsweise:
template<class T, int i>
class Matrix {
int T[i][i];
...
}
Java erlaubt nicht die Verwendung primitiver Typargumente in Generika.
Verwendung von Standardargumenten , eine Funktion, die ich in Java vermisse, für die es jedoch Gründe für die Abwärtskompatibilität gibt.
Beispielsweise:
public class ObservableList<T extends List> {
...
}
Es muss wirklich betont werden, dass Vorlagenaufrufe mit unterschiedlichen Argumenten wirklich unterschiedliche Typen sind. Sie teilen nicht einmal statische Mitglieder. In Java ist dies nicht der Fall.
Abgesehen von den Unterschieden zu Generika ist hier der Vollständigkeit halber ein grundlegender Vergleich von C ++ und Java (und einem anderen ) aufgeführt.
Und ich kann auch vorschlagen, in Java zu denken . Als C ++ - Programmierer sind viele Konzepte wie Objekte bereits selbstverständlich, aber es gibt subtile Unterschiede, sodass es sich lohnen kann, einen Einführungstext zu haben, selbst wenn Sie Teile überfliegen.
Vieles, was Sie beim Erlernen von Java lernen werden, sind alle Bibliotheken (sowohl Standard - was im JDK enthalten ist - als auch Nicht-Standard, einschließlich häufig verwendeter Dinge wie Spring). Die Java-Syntax ist ausführlicher als die C ++ - Syntax und verfügt nicht über viele C ++ - Funktionen (z. B. Überladen von Operatoren, Mehrfachvererbung, Destruktormechanismus usw.), macht sie jedoch auch nicht unbedingt zu einer Teilmenge von C ++.
Map map = new HashMap<String, String>
. Dies bedeutet, dass Sie neuen Code auf einer alten JVM bereitstellen können und dieser aufgrund der Ähnlichkeiten im Bytecode ausgeführt wird.
C ++ hat Vorlagen. Java hat Generika, die ein bisschen wie C ++ - Vorlagen aussehen, aber sie sind sehr, sehr unterschiedlich.
Vorlagen funktionieren, wie der Name schon sagt, indem sie dem Compiler eine (warten Sie ...) Vorlage zur Verfügung stellen, mit der er typsicheren Code durch Ausfüllen der Vorlagenparameter generieren kann.
Generics funktionieren, so wie ich sie verstehe, umgekehrt: Die Typparameter werden vom Compiler verwendet, um zu überprüfen, ob der Code, der sie verwendet, typsicher ist, aber der resultierende Code wird überhaupt ohne Typen generiert.
Stellen Sie sich C ++ - Vorlagen als ein wirklich gutes Makrosystem vor und Java-Generika als Werkzeug zum automatischen Generieren von Typecasts.
const
. Ein Objekt in C ++ wird nicht durch einen const
Zeiger geändert, es sei denn, die const
-ness wird weggeworfen. Ebenso ist garantiert, dass die impliziten Casts, die von generischen Typen in Java erstellt werden, "sicher" sind, es sei denn, die Typparameter werden manuell irgendwo im Code weggeworfen.
Eine weitere Funktion von C ++ - Vorlagen, die Java-Generika nicht bieten, ist die Spezialisierung. Dies ermöglicht Ihnen eine andere Implementierung für bestimmte Typen. So können Sie beispielsweise eine hochoptimierte Version für einen int haben , während Sie für den Rest der Typen immer noch eine generische Version haben. Oder Sie können verschiedene Versionen für Zeiger- und Nichtzeigertypen verwenden. Dies ist praktisch, wenn Sie das dereferenzierte Objekt bearbeiten möchten, wenn Sie einen Zeiger übergeben.
Es gibt eine großartige Erklärung zu diesem Thema in Java Generics and Collections Von Maurice Naftalin, Philip Wadler. Ich kann dieses Buch nur empfehlen. Zitieren:
Generika in Java ähneln Vorlagen in C ++. ... Die Syntax ist bewusst ähnlich und die Semantik ist bewusst unterschiedlich. ... Semantisch werden Java-Generika durch Löschen definiert, während C ++ - Vorlagen durch Erweiterung definiert werden.
Bitte lesen Sie die vollständige Erklärung hier .
(Quelle: oreilly.com )
Grundsätzlich erstellen AFAIK, C ++ - Vorlagen eine Kopie des Codes für jeden Typ, während Java-Generika genau denselben Code verwenden.
Ja, Sie können sagen, dass die C ++ - Vorlage dem generischen Java- Konzept entspricht (obwohl es besser wäre zu sagen, dass Java-Generika dem C ++ - Konzept entsprechen).
Wenn Sie mit dem Vorlagenmechanismus von C ++ vertraut sind, denken Sie vielleicht, dass Generika ähnlich sind, aber die Ähnlichkeit ist oberflächlich. Generika generieren weder für jede Spezialisierung eine neue Klasse, noch erlauben sie eine "Vorlagen-Metaprogrammierung".
von: Java Generics
Java- (und C #) - Generika scheinen ein einfacher Substitutionsmechanismus für Laufzeitarten zu sein.
C ++ - Vorlagen sind ein Konstrukt zur Kompilierungszeit, mit dem Sie die Sprache an Ihre Anforderungen anpassen können. Sie sind eigentlich eine rein funktionale Sprache, die der Compiler während einer Kompilierung ausführt.
Ein weiterer Vorteil von C ++ - Vorlagen ist die Spezialisierung.
template <typename T> T sum(T a, T b) { return a + b; }
template <typename T> T sum(T* a, T* b) { return (*a) + (*b); }
Special sum(const Special& a, const Special& b) { return a.plus(b); }
Wenn Sie nun sum mit Zeigern aufrufen, wird die zweite Methode aufgerufen, wenn Sie sum mit Nicht-Zeigerobjekten aufrufen, wird die erste Methode aufgerufen, und wenn Sie sum
mit Special
Objekten aufrufen , wird die dritte aufgerufen. Ich denke nicht, dass dies mit Java möglich ist.
Ich werde es in einem einzigen Satz zusammenfassen: Vorlagen erstellen neue Typen, Generika schränken vorhandene Typen ein.
@ Keith:
Dieser Code ist tatsächlich falsch und abgesehen von den kleineren Störungen ( template
weggelassen, Spezialisierungssyntax sieht anders aus) funktioniert die Teilspezialisierung nicht für Funktionsvorlagen, sondern nur für Klassenvorlagen. Der Code würde jedoch ohne teilweise Vorlagenspezialisierung funktionieren und stattdessen eine einfache alte Überladung verwenden:
template <typename T> T sum(T a, T b) { return a + b; }
template <typename T> T sum(T* a, T* b) { return (*a) + (*b); }
Die Antwort unten stammt aus dem Buch Cracking The Coding Interview Solutions zu Kapitel 13, das ich für sehr gut halte.
Die Implementierung von Java-Generika basiert auf der Idee des "Löschens von Typen": Diese Technik eliminiert die parametrisierten Typen, wenn der Quellcode in den Bytecode der Java Virtual Machine (JVM) übersetzt wird. Angenommen, Sie haben den folgenden Java-Code:
Vector<String> vector = new Vector<String>();
vector.add(new String("hello"));
String str = vector.get(0);
Während der Kompilierung wird dieser Code neu geschrieben in:
Vector vector = new Vector();
vector.add(new String("hello"));
String str = (String) vector.get(0);
Die Verwendung von Java-Generika hat an unseren Funktionen nicht viel geändert. es machte die Dinge nur ein bisschen schöner. Aus diesem Grund werden Java-Generika manchmal als "syntaktischer Zucker:" bezeichnet.
Dies ist ganz anders als in C ++. In C ++ sind Vorlagen im Wesentlichen ein verherrlichter Makrosatz, wobei der Compiler für jeden Typ eine neue Kopie des Vorlagencodes erstellt. Ein Beweis dafür ist die Tatsache, dass eine Instanz von MyClass keine statische Variable mit MyClass teilt. Zwei Instanzen von MyClass teilen sich jedoch eine statische Variable.
/*** MyClass.h ***/
template<class T> class MyClass {
public:
static int val;
MyClass(int v) { val v;}
};
/*** MyClass.cpp ***/
template<typename T>
int MyClass<T>::bar;
template class MyClass<Foo>;
template class MyClass<Bar>;
/*** main.cpp ***/
MyClass<Foo> * fool
MyClass<Foo> * foo2
MyClass<Bar> * barl
MyClass<Bar> * bar2
new MyClass<Foo>(10);
new MyClass<Foo>(15);
new MyClass<Bar>(20);
new MyClass<Bar>(35);
int fl fool->val; // will equal 15
int f2 foo2->val; // will equal 15
int bl barl->val; // will equal 35
int b2 bar2->val; // will equal 35
In Java werden statische Variablen unabhängig von den verschiedenen Typparametern von Instanzen von MyClass gemeinsam genutzt.
Java-Generika und C ++ - Vorlagen weisen eine Reihe weiterer Unterschiede auf. Diese beinhalten:
Vorlagen sind nichts anderes als ein Makrosystem. Syntax Zucker. Sie werden vor der eigentlichen Kompilierung vollständig erweitert (oder zumindest verhalten sich Compiler so, als ob dies der Fall wäre).
Beispiel:
Nehmen wir an, wir wollen zwei Funktionen. Eine Funktion verwendet zwei Sequenzen (Liste, Arrays, Vektoren, was auch immer) von Zahlen und gibt ihr inneres Produkt zurück. Eine andere Funktion nimmt eine Länge an, generiert zwei Sequenzen dieser Länge, übergibt sie an die erste Funktion und gibt das Ergebnis zurück. Der Haken ist, dass wir in der zweiten Funktion einen Fehler machen könnten, so dass diese beiden Funktionen nicht wirklich gleich lang sind. In diesem Fall muss der Compiler uns warnen. Nicht wenn das Programm läuft, sondern wenn es kompiliert wird.
In Java können Sie so etwas tun:
import java.io.*;
interface ScalarProduct<A> {
public Integer scalarProduct(A second);
}
class Nil implements ScalarProduct<Nil>{
Nil(){}
public Integer scalarProduct(Nil second) {
return 0;
}
}
class Cons<A implements ScalarProduct<A>> implements ScalarProduct<Cons<A>>{
public Integer value;
public A tail;
Cons(Integer _value, A _tail) {
value = _value;
tail = _tail;
}
public Integer scalarProduct(Cons<A> second){
return value * second.value + tail.scalarProduct(second.tail);
}
}
class _Test{
public static Integer main(Integer n){
return _main(n, 0, new Nil(), new Nil());
}
public static <A implements ScalarProduct<A>>
Integer _main(Integer n, Integer i, A first, A second){
if (n == 0) {
return first.scalarProduct(second);
} else {
return _main(n-1, i+1,
new Cons<A>(2*i+1,first), new Cons<A>(i*i, second));
//the following line won't compile, it produces an error:
//return _main(n-1, i+1, first, new Cons<A>(i*i, second));
}
}
}
public class Test{
public static void main(String [] args){
System.out.print("Enter a number: ");
try {
BufferedReader is =
new BufferedReader(new InputStreamReader(System.in));
String line = is.readLine();
Integer val = Integer.parseInt(line);
System.out.println(_Test.main(val));
} catch (NumberFormatException ex) {
System.err.println("Not a valid number");
} catch (IOException e) {
System.err.println("Unexpected IO ERROR");
}
}
}
In C # können Sie fast dasselbe schreiben. Versuchen Sie, es in C ++ neu zu schreiben, und es wird nicht kompiliert. Sie beschweren sich über die unendliche Erweiterung von Vorlagen.
Ich möchte hier askanydifference zitieren :
Der Hauptunterschied zwischen C ++ und Java liegt in ihrer Abhängigkeit von der Plattform. Während C ++ eine plattformabhängige Sprache ist, ist Java eine plattformunabhängige Sprache.
Die obige Anweisung ist der Grund, warum C ++ echte generische Typen bereitstellen kann. Java hat zwar strenge Überprüfungen und erlaubt daher nicht, Generika so zu verwenden, wie es C ++ erlaubt.