En C ++, un argument de référence à une fonction permet à la fonction de faire référence à quelque chose d'autre:
int replacement = 23;
void changeNumberReference(int& reference) {
reference = replacement;
}
int main() {
int i = 1;
std::cout << "i=" << i << "\n"; // i = 1;
changeNumberReference(i);
std::cout << "i=" << i << "\n"; // i = 23;
}
De façon similaire, un argument de référence constant à une fonction générera une erreur de temps de compilation si nous essayons de changer la référence:
void changeNumberReference(const int& reference) {
reference = replacement; // compile-time error: assignment of read-only reference 'reference'
}
Maintenant, avec Java, les documents disent que les arguments de fonctions de types non primitifs sont des références. Exemple tiré des documents officiels:
public void moveCircle(Circle circle, int deltaX, int deltaY) {
// code to move origin of circle to x+deltaX, y+deltaY
circle.setX(circle.getX() + deltaX);
circle.setY(circle.getY() + deltaY);
// code to assign a new reference to circle
circle = new Circle(0, 0);
}
Ensuite, cercle est assigné une référence à un nouvel objet Circle avec x = y = 0. Cette réaffectation n'a cependant pas de permanence, car la référence a été transmise par valeur et ne peut pas changer.
Pour moi, cela ne ressemble pas du tout aux références C ++. Il ne ressemble pas aux références C ++ normales parce que vous ne pouvez pas le faire référence à autre chose, et il ne ressemble pas aux références const C ++ car en Java, le code qui changerait (mais vraiment pas) la référence ne lance pas de compilation -heure d'erreur.
Ce comportement est plus similaire aux pointeurs C ++. Vous pouvez l'utiliser pour modifier les valeurs des objets pointés, mais vous ne pouvez pas modifier la valeur du pointeur elle-même dans une fonction. En outre, comme avec les pointeurs C ++ (mais pas avec les références C ++), en Java, vous pouvez passer "null" comme valeur pour un tel argument.
Ma question est donc la suivante: pourquoi Java utilise-t-il la notion de «référence»? Faut-il comprendre qu'ils ne ressemblent pas aux références C ++? Ou ressemblent-ils vraiment à des références C ++ et il me manque quelque chose?
Réponses:
Pourquoi? Parce que, même si une terminologie cohérente est généralement bonne pour l'ensemble de la profession, les concepteurs de langues ne respectent pas toujours l'utilisation des langues d'autres concepteurs de langues, en particulier si ces autres langues sont perçues comme des concurrents.
Mais vraiment, ni l'utilisation de la «référence» n'était un très bon choix. Les "références" en C ++ sont simplement une construction de langage pour introduire explicitement des alias (noms alternatifs pour exactement la même entité). Les choses auraient été beaucoup plus claires car ils avaient simplement appelé la nouvelle fonctionnalité "alias" en premier lieu. Cependant, à cette époque, la grande difficulté était de faire comprendre à tout le monde la différence entre les pointeurs (qui nécessitent un déréférencement) et les références (qui ne le font pas), donc l'important était qu'on l'appelait autre chose que "pointeur", et non pas plus précisément quel terme utiliser.
Java n'a pas de pointeurs et en est fier, donc utiliser "pointeur" comme terme n'était pas une option. Cependant, les "références" qu'il a se comportent un peu comme les pointeurs de C ++ lorsque vous les passez - la grande différence est que vous ne pouvez pas faire les opérations de bas niveau les plus méchantes (casting, ajout ...) sur eux , mais ils donnent exactement la même sémantique lorsque vous passez des poignées à des entités identiques par rapport à des entités qui se trouvent être égales. Malheureusement, le terme "pointeur" comporte tellement d'associations négatives de bas niveau qu'il est peu probable qu'il soit accepté par la communauté Java.
Le résultat est que les deux langues utilisent le même terme vague pour deux choses assez différentes, qui pourraient toutes deux bénéficier d'un nom plus spécifique, mais aucune des deux ne devrait être remplacée prochainement. Le langage naturel peut aussi parfois être frustrant!
la source
NullPointerException
du fait que le JLS utilise le terme "pointeur"?NullPointerException
n'est pas un pointeur, mais c'est une exception indiquant qu'à un niveau inférieur, un pointeur NULL a été transmis / déréférencé. L'utilisationPointer
dans le cadre d'une exception, le cas échéant, ajoute à l'image désagréable qu'ils ont.Le JLS doit mentionner des pointeurs, car la machine virtuelle de Java a été principalement écrite dans un langage qui les utilise. Vous ne pouvez pas décrire avec précision les spécifications linguistiques sans décrire le fonctionnement desUne référence est une chose qui fait référence à une autre chose. Diverses langues ont attribué un sens plus spécifique au mot «référence», généralement à «quelque chose comme un pointeur sans tous les mauvais aspects». C ++ attribue une signification spécifique, tout comme Java ou Perl.
En C ++, les références ressemblent plus à des alias (qui peuvent être implémentés via un pointeur). Cela permet de passer par référence ou de sortir des arguments .
En Java, les références sont des pointeurs sauf que ce n'est pas un concept réifié du langage: tous les objets sont des références, les types primitifs comme les nombres ne le sont pas. Ils ne veulent pas dire «pointeur» car il n'y a pas d'arithmétique de pointeur et il n'y a pas de pointeurs réifiés en Java, mais ils veulent préciser que lorsque vous passez un
Object
comme argument, l'objet n'est pas copié . Ce n'est pas non plus un passage par référence , mais quelque chose de plus comme un passage par partage .la source
Cela remonte en grande partie à Algol 68, et en partie à une réaction contre la façon dont C définit les pointeurs.
Algol 68 a défini un concept appelé référence. C'était à peu près la même chose que (pour un exemple) un pointeur en Pascal. Il s'agissait d'une cellule qui contenait NIL ou l'adresse d'une autre cellule d'un type spécifié. Vous pouvez affecter à une référence, afin qu'une référence puisse faire référence à une cellule à la fois et à une cellule différente après avoir été réaffectée. Il n'a pas , cependant, quoi que ce soit un soutien analogue à C ou arithmétique de pointeur C de.
Au moins comme Algol 68 définissait les choses, cependant, les références étaient un concept assez propre qui était assez maladroit à utiliser dans la pratique. La plupart des définitions de variables étaient en fait des références, ils ont donc défini une notation abrégée pour l'empêcher de devenir complètement incontrôlable, mais une utilisation plus banale pourrait devenir maladroite assez rapidement de toute façon.
Par exemple, une déclaration comme a
INT j := 7;
été vraiment traitée par le compilateur comme une déclaration commeREF INT j = NEW LOC INT := 7
. Donc, ce que vous avez déclaré dans Algol 68 était normalement une référence, qui était ensuite initialisée pour faire référence à quelque chose qui était alloué sur le tas, et qui était (facultativement) initialisé pour contenir une valeur spécifiée. Contrairement à Java, cependant, ils ont au moins essayé de vous laisser conserver une syntaxe saine pour cela au lieu d'avoir constamment des choses commefoo bar = new foo();
ou d'essayer de dire aux fibs que "nos pointeurs ne sont pas des pointeurs".Pascal et la plupart de ses descendants (directs et ... spirituels) ont renommé le concept de référence Algol 68 en "pointeur" mais ont gardé le concept lui-même essentiellement le même: un pointeur était une variable qui contenait l'un
nil
ou l' autre , ou l'adresse de quelque chose que vous allouiez sur le tas (c'est-à-dire, au moins tel que défini à l'origine par Jensen et Wirth, il n'y avait pas d'opérateur "adresse de", donc il n'y avait aucun moyen pour un pointeur de se référer à une variable normalement définie). Bien qu'il s'agisse de «pointeurs», aucune arithmétique des pointeurs n'était prise en charge.C et C ++ ont ajouté quelques changements à cela. Tout d' abord, ils n'ont une adresse de l' opérateur, de sorte qu'un pointeur peut se référer non seulement à quelque chose alloué sur le tas, mais à une variable, quelle que soit la façon dont il est affecté. Deuxièmement, ils définissent l'arithmétique sur les pointeurs - et définissent les indices de tableau comme simplement une notation abrégée pour l'arithmétique des pointeurs, de sorte que l'arithmétique des pointeurs est omniprésente (à côté d'inévitable) dans la plupart des C et C ++.
Lorsque Java a été inventé, Sun a apparemment pensé que "Java n'a pas de pointeurs" était un message marketing plus simple et plus propre que: "presque tout en Java est un pointeur, mais ces pointeurs sont principalement comme Pascal au lieu de C." Puisqu'ils avaient décidé que "pointeur" n'était pas un terme acceptable, ils avaient besoin d'autre chose, et ont plutôt dragué "référence", même si leurs références étaient subtilement (et dans certains cas pas si subtilement) différentes des références Algol 68.
Bien qu'il soit sorti quelque peu différemment, C ++ était coincé avec à peu près le même problème: le mot "pointeur" était déjà connu et compris, donc ils avaient besoin d'un mot différent pour cette chose différente qu'ils ajoutaient qui faisait référence à autre chose, mais était par ailleurs tout à fait un peu différent de ce que les gens entendaient par "pointeur". Ainsi, même si elle est également sensiblement différente d'une référence Algol 68, ils ont également réutilisé le terme "référence".
la source
La notion de «type de référence» est générique, elle peut exprimer à la fois un pointeur et une référence (en termes de C ++), par opposition au «type de valeur». Les références Java sont vraiment des pointeurs sans surcharge syntaxique d'
->
un*
déréférencement. Si vous regardez l'implémentation de JVM en C ++, ce sont vraiment des pointeurs nus. De plus, C # a une notion de références similaires à celles de Java, mais il a également des pointeurs et des qualificateurs 'ref' sur les paramètres de fonction, permettant de passer des types de valeurs par référence et d'éviter la copie comme&
en C ++.la source