Le mot clé new
dans des langages tels que Java, Javascript et C # crée une nouvelle instance d'une classe.
Cette syntaxe semble avoir été héritée de C++, où new
est utilisé spécifiquement pour allouer une nouvelle instance d'une classe sur le tas et renvoyer un pointeur vers la nouvelle instance. En C++, ce n'est pas la seule façon de construire un objet. Vous pouvez également construire un objet sur la pile, sans utiliser new
- et en fait, cette façon de construire des objets est beaucoup plus courante en C++.
Donc, venant d'un arrière-plan C++, le mot clé new
dans des langages comme Java, Javascript et C # m'a semblé naturel et évident. Ensuite, j'ai commencé à apprendre Python, qui n'a pas le mot clé new
. En Python, une instance est construite simplement en appelant le constructeur, comme:
f = Foo()
Au début, cela m'a semblé un peu éteint, jusqu'à ce qu'il me vienne à l'esprit qu'il n'y avait aucune raison pour que Python ait new
, parce que tout est un objet donc il n'y a pas besoin de lever l'ambiguïté entre les différentes syntaxes de constructeur.
Mais alors j'ai pensé - à quoi sert vraiment new
en Java? Pourquoi devrions-nous dire Object o = new Object();
? Pourquoi pas simplement Object o = Object();
? En C++, il y a certainement un besoin de new
, car nous devons faire la distinction entre l'allocation sur le tas et l'allocation sur la pile, mais en Java tous les objets sont construits sur le tas, alors pourquoi même avoir le mot clé new
? La même question pourrait se poser pour Javascript. En C #, que je connais beaucoup moins, je pense que new
peut avoir un certain intérêt en termes de distinction entre les types d'objets et les types de valeurs, mais je ne suis pas sûr.
Quoi qu'il en soit, il me semble que de nombreux langages qui sont venus après C++ ont simplement "hérité" du mot clé new
- sans vraiment en avoir besoin. C'est presque comme un mot-clé résiduel . Nous ne semblons pas en avoir besoin pour une raison quelconque, et pourtant il est là.
Question: Ai-je raison à ce sujet? Ou y a-t-il une raison impérieuse pour que new
doive être dans des langages gérés en mémoire inspirés de C++ comme Java, Javascript et C # mais pas Python?
Vos observations sont correctes. C++ est une bête compliquée, et le mot clé new
a été utilisé pour distinguer entre quelque chose qui nécessiterait delete
plus tard et quelque chose qui serait automatiquement récupéré. Dans Java et C #, ils ont abandonné le mot clé delete
car le garbage collector s'en chargerait pour vous.
Le problème est alors pourquoi ont-ils conservé le mot-clé new
? Sans parler aux personnes qui ont écrit la langue, il est difficile de répondre. Mes meilleures suppositions sont énumérées ci-dessous:
new
crée un objet sur le tas. Alors, pourquoi changer le comportement attendu?Ruby se situe quelque part entre Python et Java/C # dans son utilisation de new
. Fondamentalement, vous instanciez un objet comme celui-ci:
f = Foo.new()
Ce n'est pas un mot-clé, c'est une méthode statique pour la classe. Cela signifie que si vous voulez un singleton, vous pouvez remplacer l'implémentation par défaut de new()
pour renvoyer la même instance à chaque fois. Ce n'est pas nécessairement recommandé, mais c'est possible.
Bref, vous avez raison. Le nouveau mot-clé est superflu dans des langages comme Java et C #. Voici quelques idées de Bruce Eckel qui était membre du comité standard C++ dans les années 1990 et a publié plus tard des livres sur Java: http: //www.artima.com/weblogs/viewpost.jsp?thread=260578
il devait y avoir un moyen de distinguer les objets de tas des objets de pile. Pour résoudre ce problème, le nouveau mot clé a été approprié à partir de Smalltalk. Pour créer un objet de pile, il vous suffit de le déclarer, comme dans Cat x; ou, avec des arguments, Cat x ("mitaines") ;. Pour créer un objet tas, vous utilisez new, comme dans new Cat; ou nouveau chat ("mitaines");. Compte tenu des contraintes, il s'agit d'une solution élégante et cohérente.
Entrez Java, après avoir décidé que tout C++ est mal fait et trop complexe. L'ironie ici est que Java pourrait et a pris la décision de jeter l'allocation de pile (ignorant ostensiblement la débâcle des primitives, que j'ai abordée ailleurs). Et puisque tous les objets sont alloués sur le tas, il n'est pas nécessaire de faire la distinction entre la pile et l'allocation de tas. Ils auraient pu facilement dire Cat x = Cat () ou Cat x = Cat ("mitaines"). Ou mieux encore, une inférence de type incorporée pour éliminer la répétition (mais que - - et d'autres fonctionnalités telles que les fermetures - auraient pris "trop de temps", nous sommes donc coincés avec la version médiocre de Java à la place; l'inférence de type a été discutée mais je ne pense pas que ce ne sera pas le cas Et ne devrait pas, étant donné les problèmes d’ajout de nouvelles fonctionnalités à Java).
Il y a deux raisons auxquelles je peux penser:
new
fait la distinction entre un objet et une primitivenew
est un peu plus lisible (IMO)Le premier est trivial. Je ne pense pas qu'il soit plus difficile de distinguer les deux de toute façon. Le second est un exemple du point de l'OP, qui est que new
est en quelque sorte redondant.
Il pourrait cependant y avoir des conflits d'espace de noms; considérer:
public class Foo {
Foo() {
// Constructor
}
}
public class Bar {
public static void main(String[] args) {
Foo f = Foo();
}
public Foo Foo() {
return Foo();
}
}
Vous pourriez, sans trop étirer votre imagination, vous retrouver facilement avec un appel infiniment récursif. Le compilateur devrait appliquer une erreur "Nom de la fonction identique à l'objet". Cela ne ferait vraiment pas de mal, mais il est beaucoup plus simple d'utiliser new
, qui indique, Go to the object of the same name as this method and use the method that matches this signature.
Java est un langage très simple à analyser, et je soupçonne que cela aide dans ce domaine.
Java, pour sa part, a toujours la dichotomie de C++: pas tout à fait tout est un objet. Java a des types intégrés (par exemple, char
et int
) qui ne doivent pas être alloués dynamiquement.
Cela ne signifie pas que new
est vraiment nécessaire dans Java cependant - l'ensemble des types qui n'ont pas besoin d'être alloués dynamiquement est fixe et connu. Lorsque vous définissez un objet, le compilateur pourrait savoir (et, en fait, sait) si c'est une simple valeur que char
ou un objet qui doit être alloué dynamiquement.
Je m'abstiendrai (encore une fois) de dire ce que cela signifie sur la qualité de la conception de Java (ou son absence, selon le cas).
En JavaScript, vous en avez besoin car le constructeur ressemble à une fonction normale, alors comment JS devrait-il savoir que vous voulez créer un nouvel objet sans le nouveau mot-clé?
L'appel d'une fonction JavaScript avec l'opérateur new
entraîne un comportement différent de celui d'une fonction sans l'opérateur new
.
Par exemple:
Date() //Returns a string
new Date() //Returns a Date object
function foo() {};
foo() //Returns `undefined`
new foo() //Returns an empty object
J'aime beaucoup de réponses et je voudrais juste ajouter ceci:
Cela facilite la lecture du code
Non pas que cette situation se produise souvent, mais considérez que vous vouliez une méthode au nom d'un verbe qui partage le même nom qu'un nom. C # et d'autres langages ont naturellement le mot object
réservé. Mais si ce n'était pas le cas, Foo = object()
serait le résultat de l'appel de méthode objet ou serait-il une instanciation d'un nouvel objet. Espérons que le langage sans le mot clé new
a des protections contre cette situation, mais en ayant l'exigence du mot clé new
avant l'appel d'un constructeur, vous autorisez l'existence d'une méthode avec le même nom que un objet.
Fait intéressant, VB en a en a besoin - la distinction entre
Dim f As Foo
qui déclare une variable sans l'assigner, l'équivalent de Foo f;
en C #, et
Dim f As New Foo()
qui déclare la variable et assigne une nouvelle instance de la classe, l'équivalent de var f = new Foo();
en C #, est une distinction de fond. Dans pre-.NET VB, vous pouviez même utiliser Dim f As Foo
Ou Dim f As New Foo
- car il n'y avait pas de surcharge sur les constructeurs.
Il y a beaucoup de choses résiduelles dans de nombreux langages de programmation. Parfois, il est là par conception (C++ a été conçu pour être aussi compatible que possible avec C), parfois, il est juste là. Pour un exemple plus flagrant, considérez dans quelle mesure l'instruction C switch
cassée s'est propagée.
Je ne connais pas assez Javascript et C # pour le dire, mais je ne vois aucune raison pour new
in Java sauf que C++ l'a eu.
En C #, il autorise les types visibles dans des contextes où ils pourraient autrement être masqués par un membre. Le vous permet d'avoir une propriété du même nom que son type. Considérer ce qui suit,
class Address {
public string Street { get; set; }
public string City { get; set; }
public string State { get; set; }
public string Zip { get; set; }
}
class Person {
public string Name { get; set; }
public Address Address { get; set; }
public void ChangeStreet(string newStreet) {
Address = new Address {
Street = newStreet,
City = Address.City,
State = Address.State,
Zip = Address.Zip
};
}
}
Notez que l'utilisation de new
permet de préciser que Address
est le type Address
et non le membre Address
. Cela permet à un membre d'avoir le même nom que son type. Sans cela, vous devrez préfixer le nom du type pour éviter la collision de noms, comme CAddress
. C'était très intentionnel car Anders n'a jamais aimé la notation hongroise ou quelque chose de similaire et voulait que C # soit utilisable sans elle. Le fait qu'il était également familier était un double bonus.
Je ne sais pas si Java ont pensé à cela, mais quand vous pensez aux objets comme enregistrements récursifs, alors vous pouvez imaginer que Foo(123)
ne renvoie pas un objet mais une fonction dont le point fixe est l'objet en cours de création (c'est-à-dire une fonction qui retournerait l'objet s'il était donné comme argument l'objet en cours de construction). Et le but de new
est de "lier le nœud "et renvoyer ce point fixe. En d'autres termes, new
rendrait" l'objet-à-être "conscient de son self
.
Ce type d'approche pourrait aider à formaliser l'héritage par exemple: vous pouvez étendre une fonction objet avec une autre fonction objet et enfin leur fournir un self
commun en utilisant new
:
cp = new (Colored("Blue") & Line(0, 0, 100, 100))
Ici &
combine deux fonctions objet.
Dans ce cas, new
pourrait être défini comme:
def new(objectFunction) {
actualObject = objectFunction(actualObject)
return actualObject
}
Fait intéressant, avec l'avènement du transfert parfait, le non-placement new
n'est plus du tout requis en C++. Donc, sans doute, il n'est plus nécessaire dans aucune des langues mentionnées.