Pourquoi la création d'instance est-elle la même?

17

J'ai appris le C # au cours des six derniers mois environ et je suis maintenant plongé dans Java. Ma question concerne la création d'instances (dans l'une ou l'autre langue, vraiment) et c'est plus: je me demande pourquoi ils l'ont fait de cette façon. Prenez cet exemple

Person Bob = new Person();

Y a-t-il une raison pour laquelle l'objet est spécifié deux fois? Y en aurait-il jamais something_else Bob = new Person()?

Il semblerait que si je suivais la convention, ce serait plutôt:

int XIsAnInt;
Person BobIsAPerson;

Ou peut-être l'un d'eux:

Person() Bob;
new Person Bob;
new Person() Bob;
Bob = new Person();

Je suppose que je suis curieux de savoir s'il y a une meilleure réponse que "c'est juste la façon dont cela se fait".

Jason Wohlgemuth
la source
26
Et si la personne est un sous-type de LivingThing? Tu pourrais écrire LivingThing lt = new Person(). Recherchez l' héritage et les interfaces.
xlecoustillier
2
Person Bobdéclare une variable de type "référence à Person" appelée Bob. new Person()crée un Personobjet. Les références, les variables et les objets sont trois choses différentes!
user253751
5
Êtes-vous ennuyé par la redondance? Alors pourquoi ne pas écrire var bob = new Person();?
200_success
4
Person Bob();est possible en C ++ et signifie presque la même chose quePerson Bob = Person();
user60561
3
@ user60561 non, il déclare une fonction ne prenant aucun argument et renvoyant Person.
Nikolai

Réponses:

52

Y aurait-il jamais quelque chose de Bob = new Person ()?

Oui, à cause de l'héritage. Si:

public class StackExchangeMember : Person {}

Alors:

Person bob = new StackExchangeMember();
Person sam = new Person();

Bob est aussi une personne, et par golly, il ne veut pas être traité différemment des autres.

De plus, nous pourrions doter Bob de super pouvoirs:

public interface IModerator { }
public class StackOverFlowModerator : StackExchangeMember, IModerator {}

IModerator bob = new StackOverFlowModerator();

Et donc, par golly, il ne supportera pas d'être traité différemment de tout autre modérateur. Et il aime se faufiler autour du forum pour garder tout le monde en ligne tout en incognito:

StackExchangeMember bob = new StackOverFlowModerator();

Puis quand il trouve une mauvaise première affiche, il jette sa cape d'invisibilité et bondit.

((StackOverFlowModerator) bob).Smite(sam);

Et puis il peut agir tout innocent et tout ça après:

((Person) bob).ImNotMeanIWasJustInstantiatedThatWay();
radarbob
la source
20
Ce serait beaucoup plus clair si vous mettiez en minuscule les noms de vos objets.
Courses de légèreté avec Monica le
38
Bon exemple de spécification; mauvais exemple d'héritage. À toute personne lisant ceci, n'essayez pas de résoudre les rôles utilisateur à l'aide de l'héritage.
Aaronaught
8
@Aaronaught est correct. Ne créez pas de classes distinctes pour différents types de personnes. Utilisez un champ de bits enum.
Cole Johnson
1
@Aaronaught C'est très bien de dire quoi ne pas faire, mais ce n'est pas très utile sans dire ce que les gens devraient faire à la place.
Pharap
5
@Pharap: J'ai fait exactement cela, dans plusieurs autres questions . La réponse est simple: les utilisateurs (authentification / identité) et la politique de sécurité (autorisation / autorisations) doivent être traités comme des préoccupations distinctes, et les modèles standard de politique de sécurité sont basés sur les rôles ou sur les revendications. L'héritage est plus utile pour décrire l'objet qui effectue réellement l'authentification, par exemple une implémentation LDAP et une implémentation SQL.
Aaronaught
34

Prenons votre première ligne de code et examinons-la.

Person Bob = new Person();

Le premier Personest une spécification de type. En C #, on peut se passer de cela en disant simplement

var Bob = new Person();

et le compilateur déduira le type de la variable Bob à partir de l'appel du constructeur Person().

Mais vous voudrez peut-être écrire quelque chose comme ceci:

IPerson Bob = new Person();

Lorsque vous ne remplissez pas l'intégralité du contrat API de Person, mais uniquement le contrat spécifié par l'interface IPerson.

Robert Harvey
la source
2
+1: Je vais faire l' IPersonexemple dans mon code pour m'assurer de ne pas utiliser accidentellement de méthodes privées lorsque j'écris du code qui devrait être copié / collé dans une autre IPersonimplémentation.
Cort Ammon - Reinstate Monica
@CortAmmon Je pense que vous avez une faute de frappe, clairement vous vouliez dire "travailler avec le polymorphisme" plutôt que copier / coller sur ce code: D
Benjamin Gruenbaum
@BenjaminGruenbaum bien sûr, pour une définition du polymorphisme ;-)
Cort Ammon - Reinstate Monica
@CortAmmon comment appelleriez-vous accidentellement une méthode privée ? Vous vouliez sûrement dire internal?
Cole Johnson
@ColeJohnson dans les deux cas. J'ai écrit cela en pensant à un cas spécifique que je traverse où privateest significatif: les méthodes d'usine qui font partie de la classe en cours d'instanciation. Dans ma situation, l'accès aux valeurs privées devrait être l'exception et non la norme. Je travaille sur du code qui va me survivre longtemps. Si je le fais de cette façon, non seulement il est peu probable que j'utilise moi-même des méthodes privées, mais lorsque le prochain développeur copie / colle ceci quelques dizaines d'endroits et que le développeur les copie ensuite, cela diminue les chances que quelqu'un voit une opportunité de utiliser des méthodes privées comme comportement "normal".
Cort Ammon - Reinstate Monica
21
  1. Cette syntaxe est à peu près un héritage de C ++, qui, en passant, a les deux:

    Person Bob;

    et

    Person *bob = new Bob();

    Le premier pour créer un objet dans la portée actuelle, le second pour créer un pointeur vers un objet dynamique.

  2. Vous pouvez certainement avoir something_else Bob = new Person()

    IEnumerable<int> nums = new List<int>(){1,2,3,4}

    Vous faites deux choses différentes ici, en indiquant le type de la variable locale numset vous dites que vous voulez créer un nouvel objet du type 'List' et le mettre là.

  3. C # est en quelque sorte d'accord avec vous, car la plupart du temps le type de la variable est identique à ce que vous y mettez donc:

    var nums = new List<int>();
  4. Dans certaines langues, vous faites de votre mieux pour éviter d'indiquer les types de variables comme dans F # :

    let list123 = [ 1; 2; 3 ]
AK_
la source
5
Il est probablement plus exact de dire que votre deuxième exemple crée un pointeur vers un nouvel objet Bob. La façon dont les choses sont stockées est techniquement un détail d'implémentation.
Robert Harvey
4
"Le premier à créer un objet local sur la pile, le second à créer un objet sur le tas." Oh mec, pas encore cette désinformation.
Courses de légèreté avec Monica le
@LightnessRacesinOrbit mieux?
AK_
1
@AK_ Alors que "dynamique" signifie à peu près "tas" (lorsque le tas est le modèle de mémoire utilisé par la plate-forme), "automatique" est très différent de "pile". Si vous le faites new std::pair<int, char>(), les membres firstet secondla paire ont une durée de stockage automatique, mais ils sont probablement alloués sur le tas (en tant que membres de l'objet dynamic-storage-duration pair).
Rétablir Monica le
1
@AK_: Oui, ces noms impliquent précisément ce qu'ils signifient, alors que cette « pile » vs non - sens « tas » ne pas . C'est tout le problème. Le fait que vous les trouviez confus ne fait que renforcer la nécessité pour nous de les enseigner, de sorte que vous ne vous reposez pas sur une terminologie inexacte / incorrecte simplement parce qu'elle est familière!
Courses de légèreté avec Monica le
3

Il y a une énorme différence entre int xet Person bob. Un intest un intest un intet il doit toujours être un intet ne peut jamais être autre chose qu'un int. Même si vous n'initialisez pas le intlorsque vous le déclarez ( int x;), c'est toujours unint ensemble à la valeur par défaut.

Person bobCependant, lorsque vous déclarez , il y a une grande flexibilité quant à ce à quoi le nom bobpourrait réellement faire référence à un moment donné. Elle pourrait faire référence à une Person, ou elle pourrait se référer à une autre classe, par exemple Programmer, dérivée de Person; ça pourrait même êtrenull , ne faisant référence à aucun objet.

Par exemple:

  Person bob   = null;
  Person carol = new Person();
  Person ted   = new Programmer();
  Person alice = personFactory.functionThatReturnsSomeKindOfPersonOrNull();

Les concepteurs de langage auraient certainement pu créer une syntaxe alternative qui aurait accompli la même chose que Person carol = new Person()dans moins de symboles, mais ils auraient quand même dû autoriser Person carol = new Person()(ou rendre une règle étrange rendant ce particulier des quatre exemples ci-dessus illégal). Ils étaient plus soucieux de garder le langage "simple" que d'écrire du code extrêmement concis. Cela a peut-être influencé leur décision de ne pas fournir la syntaxe alternative la plus courte, mais en tout cas, ce n'était pas nécessaire et ils ne l'ont pas fournie.

David K
la source
1

Les deux déclarations peuvent être différentes mais sont souvent les mêmes. Un modèle courant et recommandé en Java ressemble à ceci:

List<String> list = new ArrayList<>();
Map<String, Integer> map = new HashMap<>();

Ces variables listet mapsont déclarées à l'aide des interfacesList et Maptandis que le code instancie des implémentations spécifiques. De cette façon, le reste du code ne dépend que des interfaces et il est facile de choisir une classe d'implémentation différente à instancier, comme TreeMap, car le reste du code ne peut dépendre d'aucune partie de l' HashMapAPI qui se trouve en dehors de l' Mapinterface.

Un autre exemple où les deux types diffèrent est dans une méthode d'usine qui sélectionne une sous-classe spécifique à instancier, puis la renvoie comme type de base afin que l'appelant n'ait pas besoin d'être au courant des détails d'implémentation, par exemple un choix de "politique".

L'inférence de type peut corriger la redondance du code source. Par exemple en Java

List<String> listOne = Collections.emptyList();

va construire le bon type de liste grâce à l'inférence de type et à la déclaration

static <T> List<T> emptyList(); 

Dans certains langages, l'inférence de type va plus loin, par exemple en C ++

auto p = new Person();
Jerry101
la source
BTW le langage Java définit une convention forte pour l'utilisation de noms d'identificateurs en minuscules, comme bob, non Bob. Cela évite beaucoup d'ambiguïté, par exemple package.Class vs Class.variable.
Jerry101
... et c'est pourquoi vous l'avez fait clazz.
un CVn du
Non, clazzest utilisé car classest un mot-clé et ne peut donc pas être utilisé comme identifiant.
Jerry101
... ce qui ne serait pas un problème si les conventions de nommage n'étaient pas telles qu'elles sont. Classest un identifiant parfaitement valide.
cHao
... tels quels cLaSs, cLASSet cLASs.
el.pescado
1

Dans les mots du profane:

  • Séparer la déclaration de l'instanciation permet de découpler qui utilise les objets de qui les crée
  • Lorsque vous faites cela, le polyporphisme est activé car, tant que le type instancié est un sous-type du type de déclaration, tout le code utilisant la variable fonctionnera
  • Dans les langages fortement typés, vous devez déclarer une variable en indiquant son type, en faisant simplement, var = new Process()vous ne déclarez pas la variable en premier.
Tulains Córdova
la source
0

Il s'agit également du niveau de contrôle sur ce qui se passe. Si la déclaration d'un objet / variable appelle automatiquement un constructeur, par exemple, si

Person somePerson;

était automatiquement le même que

Person somePerson = new Person(blah, blah..);

alors vous ne pourriez jamais utiliser (par exemple) des méthodes d'usine statiques pour instancier des objets plutôt que des constructeurs par défaut, c'est-à-dire qu'il y a des moments où vous ne voulez pas appeler un constructeur pour une nouvelle instance d'objet.

Cet exemple est expliqué dans Joshua Bloch 's Effective Java (Item 1 assez ironiquement!)

David Scholefield
la source
En ce qui concerne les livres, mon livre C # et mon livre Java provenaient de Joyce Farrell. C'est exactement ce que le cours a spécifié. J'ai également complété à la fois avec diverses vidéos youtube sur C # et Java.
Jason Wohlgemuth
Je ne critiquais pas, je donnais juste une référence d'où cela venait :)
David Scholefield