Réponses:
Il y a une grande différence entre eux. En C ++, vous n'avez pas besoin de spécifier une classe ou une interface pour le type générique. C'est pourquoi vous pouvez créer des fonctions et des classes vraiment génériques, avec la mise en garde d'un typage plus lâche.
template <typename T> T sum(T a, T b) { return a + b; }
La méthode ci-dessus ajoute deux objets du même type et peut être utilisée pour tout type T pour lequel l'opérateur "+" est disponible.
En Java, vous devez spécifier un type si vous souhaitez appeler des méthodes sur les objets passés, quelque chose comme:
<T extends Something> T sum(T a, T b) { return a.add ( b ); }
En C ++, les fonctions / classes génériques ne peuvent être définies que dans les en-têtes, car le compilateur génère différentes fonctions pour différents types (avec lesquels il est appelé). La compilation est donc plus lente. En Java, la compilation n'a pas de pénalité majeure, mais Java utilise une technique appelée "effacement" où le type générique est effacé au moment de l'exécution, donc au moment de l'exécution, Java appelle en fait ...
Something sum(Something a, Something b) { return a.add ( b ); }
La programmation générique en Java n'est donc pas vraiment utile, ce n'est qu'un peu de sucre syntaxique pour aider avec la nouvelle construction foreach.
EDIT: l'avis ci-dessus sur l'utilité a été rédigé par un moi plus jeune. Les génériques de Java aident bien sûr à la sécurité de type.
extends
ou l' autre super
. La réponse est incorrecte,
Les génériques Java sont très différents des modèles C ++.
Fondamentalement, les modèles C ++ sont essentiellement un ensemble préprocesseur / macro glorifié ( Remarque: puisque certaines personnes semblent incapables de comprendre une analogie, je ne dis pas que le traitement des modèles est une macro). En Java, ils sont essentiellement du sucre syntaxique pour minimiser la conversion standard des objets. Voici une introduction assez décente aux modèles C ++ par rapport aux génériques Java .
Pour élaborer sur ce point: lorsque vous utilisez un modèle C ++, vous créez essentiellement une autre copie du code, comme si vous utilisiez une #define
macro. Cela vous permet de faire des choses comme avoir des int
paramètres dans les définitions de modèle qui déterminent la taille des tableaux, etc.
Java ne fonctionne pas comme ça. En Java, tous les objets s'étendent à partir de java.lang.Object donc, pré-génériques, vous écririez du code comme celui-ci:
public class PhoneNumbers {
private Map phoneNumbers = new HashMap();
public String getPhoneNumber(String name) {
return (String)phoneNumbers.get(name);
}
...
}
car tous les types de collection Java utilisaient Object comme type de base afin que vous puissiez y mettre n'importe quoi. Java 5 roule et ajoute des génériques pour que vous puissiez faire des choses comme:
public class PhoneNumbers {
private Map<String, String> phoneNumbers = new HashMap<String, String>();
public String getPhoneNumber(String name) {
return phoneNumbers.get(name);
}
...
}
Et c'est tout ce que sont les génériques Java: des wrappers pour la diffusion d'objets. C'est parce que les génériques Java ne sont pas raffinés. Ils utilisent l'effacement de caractères. Cette décision a été prise parce que Java Generics est arrivé si tard dans la pièce qu'ils ne voulaient pas interrompre la rétrocompatibilité (a Map<String, String>
est utilisable chaque fois que a Map
est demandé). Comparez cela à .Net / C # où l'effacement de type n'est pas utilisé, ce qui conduit à toutes sortes de différences (par exemple, vous pouvez utiliser des types primitifs et IEnumerable
et IEnumerable<T>
n'avoir aucun rapport entre eux).
Et une classe utilisant des génériques compilée avec un compilateur Java 5+ est utilisable sur JDK 1.4 (en supposant qu'elle n'utilise aucune autre fonctionnalité ou classe nécessitant Java 5+).
C'est pourquoi les génériques Java sont appelés sucre syntaxique .
Mais cette décision sur la façon de faire des génériques a des effets tellement profonds que la (superbe) FAQ sur les génériques Java a vu le jour pour répondre aux nombreuses questions que les gens se posent sur les génériques Java.
Les modèles C ++ ont un certain nombre de fonctionnalités que Java Generics n'a pas:
Utilisation d'arguments de type primitif.
Par exemple:
template<class T, int i>
class Matrix {
int T[i][i];
...
}
Java n'autorise pas l'utilisation d'arguments de type primitif dans les génériques.
Utilisation d' arguments de type par défaut , qui est une fonctionnalité qui me manque dans Java, mais il y a des raisons de compatibilité ascendante à cela;
Par exemple:
public class ObservableList<T extends List> {
...
}
Il faut vraiment souligner que les invocations de modèles avec des arguments différents sont vraiment de types différents. Ils ne partagent même pas les membres statiques. En Java, ce n'est pas le cas.
Outre les différences avec les génériques, pour être complet, voici une comparaison de base de C ++ et Java (et une autre ).
Et je peux également suggérer Penser en Java . En tant que programmeur C ++, beaucoup de concepts comme les objets seront déjà une seconde nature, mais il y a des différences subtiles, il peut donc être intéressant d'avoir un texte d'introduction même si vous en parcourez des parties.
Une grande partie de ce que vous apprendrez en apprenant Java, ce sont toutes les bibliothèques (à la fois standard - ce qui vient dans le JDK - et non standard, qui comprend des éléments couramment utilisés comme Spring). La syntaxe Java est plus verbeuse que la syntaxe C ++ et n'a pas beaucoup de fonctionnalités C ++ (par exemple, la surcharge d'opérateurs, l'héritage multiple, le mécanisme de destruction, etc.) mais cela n'en fait pas non plus strictement un sous-ensemble de C ++.
Map map = new HashMap<String, String>
. Cela signifie que vous pouvez déployer un nouveau code sur une ancienne JVM et qu'il fonctionnera en raison des similitudes du bytecode.
C ++ a des modèles. Java a des génériques, qui ressemblent un peu aux modèles C ++, mais ils sont très, très différents.
Les modèles fonctionnent, comme leur nom l'indique, en fournissant au compilateur un modèle (attendez-le ...) qu'il peut utiliser pour générer du code de type sécurisé en remplissant les paramètres du modèle.
Les génériques, tels que je les comprends, fonctionnent à l'inverse: les paramètres de type sont utilisés par le compilateur pour vérifier que le code qui les utilise est de type sécurisé, mais le code résultant est généré sans aucun type.
Considérez les modèles C ++ comme un très bon système de macros et les génériques Java comme un outil pour générer automatiquement des typecasts.
const
. Un objet en C ++ ne sera pas modifié via un const
pointeur à moins que const
-ness ne soit casté. De même, les transtypages implicites créés par des types génériques en Java sont garantis "sûrs" à moins que les paramètres de type ne soient manuellement rejetés quelque part dans le code.
Une autre fonctionnalité des modèles C ++ que les génériques Java n'ont pas est la spécialisation. Cela vous permet d'avoir une implémentation différente pour des types spécifiques. Ainsi, vous pouvez, par exemple, avoir une version hautement optimisée pour un int , tout en ayant toujours une version générique pour le reste des types. Ou vous pouvez avoir différentes versions pour les types pointeur et non pointeur. Ceci est pratique si vous souhaitez opérer sur l'objet déréférencé lorsque vous recevez un pointeur.
Il y a une bonne explication de ce sujet dans les génériques Java et les collections par Maurice Naftalin, Philip Wadler. Je recommande vivement ce livre. Citer:
Les génériques en Java ressemblent aux modèles en C ++. ... La syntaxe est volontairement similaire et la sémantique est volontairement différente. ... Sémantiquement, les génériques Java sont définis par effacement, alors que les modèles C ++ sont définis par expansion.
Veuillez lire l'explication complète ici .
(source: oreilly.com )
Fondamentalement, les modèles AFAIK et C ++ créent une copie du code pour chaque type, tandis que les génériques Java utilisent exactement le même code.
Oui, vous pouvez dire que le modèle C ++ est équivalent au concept générique Java (bien que plus correctement serait de dire que les génériques Java sont équivalents au concept C ++)
Si vous êtes familier avec le mécanisme de template de C ++, vous pourriez penser que les génériques sont similaires, mais la similitude est superficielle. Les génériques ne génèrent pas de nouvelle classe pour chaque spécialisation et ne permettent pas la «métaprogrammation de modèles».
à partir de: Java Generics
Les génériques Java (et C #) semblent être un simple mécanisme de substitution de type à l'exécution.
Les modèles C ++ sont une construction au moment de la compilation qui vous permet de modifier le langage en fonction de vos besoins. Il s'agit en fait d'un langage purement fonctionnel que le compilateur exécute lors d'une compilation.
Un autre avantage des modèles C ++ est la spécialisation.
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); }
Maintenant, si vous appelez sum avec des pointeurs, la deuxième méthode sera appelée, si vous appelez sum avec des objets non pointeurs, la première méthode sera appelée, et si vous appelez sum
avec des Special
objets, la troisième sera appelée. Je ne pense pas que cela soit possible avec Java.
Je vais le résumer en une seule phrase: les modèles créent de nouveaux types, les génériques restreignent les types existants.
@Keith:
Ce code est en fait faux et mis à part les petits problèmes ( template
omis, la syntaxe de spécialisation a un aspect différent), la spécialisation partielle ne fonctionne pas sur les modèles de fonction, uniquement sur les modèles de classe. Le code fonctionnerait cependant sans spécialisation partielle du modèle, en utilisant plutôt une ancienne surcharge:
template <typename T> T sum(T a, T b) { return a + b; }
template <typename T> T sum(T* a, T* b) { return (*a) + (*b); }
La réponse ci-dessous est tirée du livre Cracking The Coding Interview Solutions au chapitre 13, ce que je trouve très bien.
L'implémentation des génériques Java est enracinée dans une idée de "l'effacement de type:" Cette technique élimine les types paramétrés lorsque le code source est traduit en bytecode Java Virtual Machine (JVM). Par exemple, supposons que vous ayez le code Java ci-dessous:
Vector<String> vector = new Vector<String>();
vector.add(new String("hello"));
String str = vector.get(0);
Lors de la compilation, ce code est réécrit dans:
Vector vector = new Vector();
vector.add(new String("hello"));
String str = (String) vector.get(0);
L'utilisation des génériques Java n'a pas vraiment changé grand-chose à nos capacités; ça rendait les choses un peu plus jolies. Pour cette raison, les génériques Java sont parfois appelés "sucre syntaxique: '.
C'est assez différent du C ++. En C ++, les modèles sont essentiellement un ensemble de macros glorifié, le compilateur créant une nouvelle copie du code du modèle pour chaque type. La preuve en est qu'une instance de MyClass ne partagera pas une variable statique avecMyClass. Cependant, les instances de remorquage de MyClass partageront une variable statique.
/*** 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
En Java, les variables statiques sont partagées entre les instances de MyClass, quels que soient les di érents paramètres de type.
Les génériques Java et les modèles C ++ présentent un certain nombre d'autres différences. Ceux-ci inclus:
Les modèles ne sont rien d'autre qu'un système de macros. Sucre de syntaxe. Ils sont entièrement développés avant la compilation réelle (ou, du moins, les compilateurs se comportent comme si c'était le cas).
Exemple:
Disons que nous voulons deux fonctions. Une fonction prend deux séquences (liste, tableaux, vecteurs, tout ce qui se passe) de nombres et renvoie leur produit interne. Une autre fonction prend une longueur, génère deux séquences de cette longueur, les transmet à la première fonction et renvoie son résultat. Le hic, c'est que nous pourrions faire une erreur dans la deuxième fonction, de sorte que ces deux fonctions ne sont pas vraiment de la même longueur. Nous avons besoin du compilateur pour nous avertir dans ce cas. Pas lorsque le programme est en cours d'exécution, mais lors de sa compilation.
En Java, vous pouvez faire quelque chose comme ceci:
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");
}
}
}
En C #, vous pouvez écrire presque la même chose. Essayez de le réécrire en C ++, et il ne se compilera pas, vous plaignant de l'expansion infinie des modèles.
Je voudrais citer askanydifference ici:
La principale différence entre C ++ et Java réside dans leur dépendance à la plate-forme. Alors que C ++ est un langage dépendant de la plateforme, Java est un langage indépendant de la plateforme.
L'instruction ci-dessus est la raison pour laquelle C ++ est capable de fournir de vrais types génériques. Bien que Java ait une vérification stricte et par conséquent, ils n'autorisent pas l'utilisation de génériques comme le permet C ++.