Frage

Java verfügt über Generika und C++ bietet ein sehr starkes Programmiermodell templateS.Was ist dann der Unterschied zwischen C++- und Java-Generika?

War es hilfreich?

Lösung

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 erstellen, mit dem Vorbehalt einer lockeren Typisierung.

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, etwa:

<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 unterschiedliche Funktionen für unterschiedliche Typen generiert (mit denen er aufgerufen wird).Die Kompilierung ist also langsamer.In Java bringt die Kompilierung keine großen Nachteile mit sich, aber Java verwendet eine Technik namens „Erasure“, bei der der generische Typ zur Laufzeit gelöscht wird, sodass Java zur Laufzeit tatsächlich aufruft ...

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, der beim neuen foreach-Konstrukt hilft.

BEARBEITEN: Die obige Meinung zur Nützlichkeit wurde von einem jüngeren Ich verfasst.Die Generika von Java helfen natürlich bei der Typsicherheit.

Andere Tipps

Java-Generika sind massiv Anders als C++-Vorlagen.

Grundsätzlich handelt es sich bei C++-Vorlagen um einen verschönerten Präprozessor-/Makrosatz (Notiz: Da einige Leute anscheinend nicht in der Lage sind, eine Analogie zu verstehen, sage ich nicht, dass die Vorlagenverarbeitung ein Makro ist.In Java handelt es sich im Grunde genommen um syntaktischen Zucker, um die Standardumwandlung von Objekten zu minimieren.Hier ist eine ziemlich anständige Einführung in C++-Vorlagen im Vergleich zu Java-Generika.

Um diesen Punkt näher zu erläutern:Wenn Sie eine C++-Vorlage verwenden, erstellen Sie im Grunde eine weitere Kopie des Codes, genau wie wenn Sie eine verwenden würden #define Makro.Auf diese Weise können Sie Dinge wie „Haben“ tun int Parameter in Vorlagendefinitionen, 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 diesen 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 man alles in sie einfügen konnte.Java 5 rollt herum und fügt Generika hinzu, sodass 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:Hüllen zum Gießen von Gegenständen.Das liegt daran, dass Java Generics nicht verfeinert sind.Sie verwenden Typlöschung.Diese Entscheidung wurde getroffen, weil Java Generics so spät in den Artikel kam, dass man die Abwärtskompatibilität nicht beeinträchtigen wollte (a Map<String, String> ist immer dann verwendbar, wenn a Map gefordert ist).Vergleichen Sie dies mit .Net/C#, wo keine Typlöschung verwendet wird, was zu allen möglichen Unterschieden führt (z. B.Sie können primitive Typen und verwenden IEnumerable Und IEnumerable<T> stehen in keiner Beziehung zueinander).

Und eine Klasse, die Generics verwendet und mit einem Java 5+-Compiler kompiliert wurde, ist auf JDK 1.4 verwendbar (vorausgesetzt, sie verwendet keine anderen Funktionen oder Klassen, die Java 5+ erfordern).

Deshalb werden Java Generics genannt syntethischer Zucker.

Aber diese Entscheidung darüber, wie man Generika macht, hat so tiefgreifende Auswirkungen, dass die (hervorragende) Häufig gestellte Fragen zu Java Generics ist entstanden, um die vielen, vielen Fragen zu beantworten, die Menschen zu Java Generics haben.

C++-Vorlagen verfügen über eine Reihe von Funktionen, die Java Generics nicht bieten:

  • Verwendung von Argumenten primitiven Typs.

    Zum Beispiel:

    template<class T, int i>
    class Matrix {
      int T[i][i];
      ...
    }
    

    Java erlaubt die Verwendung von Argumenten primitiver Typen in Generika nicht.

  • Gebrauch von Standardtypargumente, Dies ist eine Funktion, die ich in Java vermisse, aber dafür gibt es Gründe für die Abwärtskompatibilität.

  • Java erlaubt die Begrenzung von Argumenten.

Zum Beispiel:

public class ObservableList<T extends List> {
  ...
}

Es muss wirklich betont werden, dass es sich bei Vorlagenaufrufen mit unterschiedlichen Argumenten tatsächlich um unterschiedliche Typen handelt.Sie teilen nicht einmal statische Mitglieder.In Java ist dies nicht der Fall.

Abgesehen von den Unterschieden zu Generika finden Sie hier der Vollständigkeit halber Folgendes: grundlegender Vergleich von C++ und Java (Und noch einer).

Und ich kann es auch vorschlagen Denken in Java.Für einen C++-Programmierer sind viele Konzepte wie Objekte bereits selbstverständlich, es gibt jedoch subtile Unterschiede, sodass es sich lohnen kann, einen Einführungstext zu haben, auch wenn Sie Teile überfliegen.

Beim Erlernen von Java werden Sie vor allem die Bibliotheken lernen (sowohl Standardbibliotheken, die im JDK enthalten sind, als auch Nichtstandardbibliotheken, zu denen häufig verwendete Dinge wie Spring gehören).Die Java-Syntax ist ausführlicher als die C++-Syntax und verfügt nicht über viele C++-Funktionen (z. B.Operatorüberladung, Mehrfachvererbung, Destruktormechanismus usw.), aber das macht es auch nicht unbedingt zu einer Teilmenge von C++.

C++ verfügt über Vorlagen.Java verfügt über Generika, die ein bisschen wie C++-Vorlagen aussehen, aber sehr, sehr unterschiedlich sind.

Vorlagen funktionieren, wie der Name schon sagt, indem sie dem Compiler eine (warten Sie darauf...) Vorlage zur Verfügung stellen, die er verwenden kann, um typsicheren Code zu generieren, indem er die Vorlagenparameter ausfüllt.

So wie ich sie verstehe, funktionieren Generika umgekehrt:Die Typparameter werden vom Compiler verwendet, um zu überprüfen, ob der Code, der sie verwendet, typsicher ist. Der resultierende Code wird jedoch überhaupt ohne Typen generiert.

Stellen Sie sich C++-Vorlagen als eine vor wirklich gut Makrosystem und Java-Generika als Werkzeug zur automatischen Generierung von Typumwandlungen.

 

Eine weitere Funktion von C++-Vorlagen, die Java-Generika nicht bieten, ist die Spezialisierung.Dadurch können Sie für bestimmte Typen eine unterschiedliche Implementierung verwenden.So können Sie beispielsweise eine hochoptimierte Version für eine haben int, während es für die übrigen Typen immer noch eine generische Version gibt.Oder Sie können unterschiedliche Versionen für Zeiger- und Nicht-Zeigertypen verwenden.Dies ist praktisch, wenn Sie das dereferenzierte Objekt bearbeiten möchten, wenn Ihnen ein Zeiger übergeben wird.

Es gibt eine tolle Erklärung zu diesem Thema in Java-Generika und SammlungenVon Maurice Naftalin, Philip Wadler.Ich kann dieses Buch wärmstens empfehlen.Zitieren:

Generika in Java ähneln Vorlagen in C ++....Die Syntax ist absichtlich ähnlich und die Semantik unterscheidet sich absichtlich....Semantisch sind Java -Generika durch Löschung definiert, wobei als C ++ - Vorlagen durch Expansion definiert werden.

Bitte lesen Sie die vollständige Erklärung Hier.

alt text
(Quelle: oreilly.com)

Grundsätzlich erstellen AFAIK- und C++-Vorlagen eine Kopie des Codes für jeden Typ, während Java-Generika genau denselben Code verwenden.

Ja du kann sagen Diese C++-Vorlage entspricht dem Java-Generikum Konzept (obwohl es richtiger wäre zu sagen, dass Java-Generika vom Konzept her äquivalent zu C++ sind)

Wenn Sie mit dem Vorlagenmechanismus von C++ vertraut sind, denken Sie vielleicht, dass Generika ähnlich sind, aber die Ähnlichkeit ist oberflächlich.Generics generieren weder für jede Spezialisierung eine neue Klasse, noch ermöglichen sie eine „Vorlagen-Metaprogrammierung“.

aus: Java-Generika

Java- (und C#-)Generika scheinen ein einfacher Typersetzungsmechanismus zur Laufzeit zu sein.
C++-Vorlagen sind ein Konstrukt zur Kompilierungszeit, mit dem Sie die Sprache an Ihre Bedürfnisse 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-Zeiger-Objekten aufrufen, wird die erste Methode aufgerufen, und wenn Sie „sum“ aufrufen, wird die erste Methode aufgerufen sum mit Special Objekte, das dritte wird aufgerufen.Ich glaube nicht, dass dies mit Java möglich ist.

Ich fasse es in einem einzigen Satz zusammen:Vorlagen erstellen neue Typen, Generika schränken vorhandene Typen ein.

@Keith:

Dieser Code ist tatsächlich falsch und abgesehen von den kleineren Fehlern (template weggelassen, Spezialisierungssyntax sieht anders aus), teilweise Spezialisierung nicht Arbeiten Sie an Funktionsvorlagen, nur an Klassenvorlagen.Der Code würde jedoch ohne teilweise Template-Spezialisierung funktionieren und stattdessen die 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 Lösungen zu Kapitel 13, die ich sehr gut finde.

Die Implementierung von Java-Generika basiert auf der Idee der „Typlöschung“: Diese Technik eliminiert die parametrisierten Typen, wenn 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 Fähigkeiten nicht viel geändert;es hat die Sache einfach ein bisschen schöner gemacht.Aus diesem Grund werden Java-Generika manchmal als „syntaktischer Zucker:“ bezeichnet.

Das unterscheidet sich deutlich von C++.In C++ sind Vorlagen im Wesentlichen ein verschönerter 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 allen Instanzen von MyClass gemeinsam genutzt.

Java-Generika und C++-Vorlagen weisen eine Reihe weiterer Unterschiede auf.Diese beinhalten:

  • C++-Vorlagen können primitive Typen wie int verwenden.Java kann und muss stattdessen Integer verwenden.
  • In Java können Sie die Typparameter der Vorlage so einschränken, dass sie einen bestimmten Typ haben.Beispielsweise können Sie Generics verwenden, um einen Kartendecke zu implementieren und anzugeben, dass sich der Typ -Parameter von Cardgame erstrecken muss.
  • In C ++ kann der Typ -Parameter instanziiert werden, während Java dies nicht unterstützt.
  • In Java kann der Typparameter (dh der Foo in MyClass) nicht für statische Methoden und Variablen verwendet werden, da diese zwischen Myclass und Myclass geteilt würden.In C++ sind diese Klassen unterschiedlich, sodass der Typparameter für statische Methoden und Variablen verwendet werden kann.
  • In Java sind alle Instanzen von MyClass unabhängig von ihren Typparametern vom gleichen Typ.Die Typparameter werden zur Laufzeit gelöscht.In C++ sind Instanzen mit unterschiedlichen Typparametern unterschiedliche Typen.

Vorlagen sind nichts anderes als ein Makrosystem.Syntaxzucker.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 nimmt zwei Folgen (Liste, Arrays, Vektoren, was auch immer) von Zahlen und gibt deren inneres Produkt zurück.Eine andere Funktion benötigt eine Länge, generiert zwei Sequenzen dieser Länge, übergibt sie an die erste Funktion und gibt ihr Ergebnis zurück.Der Haken ist, dass wir bei der zweiten Funktion möglicherweise einen Fehler machen, sodass diese beiden Funktionen nicht wirklich die gleiche Länge haben.In diesem Fall benötigen wir eine Warnung des Compilers.Nicht, wenn das Programm ausgeführt wird, 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, aber es lässt sich nicht kompilieren und beschwert sich über die unendliche Erweiterung der Vorlagen.

Lizenziert unter: CC-BY-SA mit Zuschreibung
Nicht verbunden mit StackOverflow
scroll top