Pourquoi une méthode principale statique en Java et en C # plutôt qu'un constructeur?

54

Je cherche une réponse définitive d'une source primaire ou secondaire expliquant pourquoi (notamment) Java et C # ont décidé de choisir une méthode statique comme point d'entrée, plutôt que de représenter une instance d'application par une instance d'une Applicationclasse (avec le point d'entrée être un constructeur approprié).


Contexte et détails de mes recherches antérieures

Cela a été demandé auparavant. Malheureusement, les réponses existantes ne font que poser la question . En particulier, les réponses suivantes ne me satisfont pas, car je les juge incorrectes:

  • Il y aurait une ambiguïté si le constructeur était surchargé. - En fait, C # (ainsi que C et C ++) autorise différentes signatures, de Mainsorte que la même ambiguïté existe et est traitée.
  • Une staticméthode signifie qu'aucun objet ne peut être instancié avant que l'ordre d'initialisation soit clair. - Ceci est faux, certains objets sont instanciés avant (par exemple dans un constructeur statique).
  • Ils peuvent donc être appelés par le runtime sans avoir à instancier un objet parent. - Ce n'est pas une réponse du tout.

Juste pour justifier davantage pourquoi je pense que cette question est valable et intéressante:

  • De nombreux cadres font utiliser des classes pour représenter les applications et les constructeurs comme points d'entrée. Par exemple, l' infrastructure d'application VB.NET utilise un dialogue principal dédié (et son constructeur) comme point d'entrée 1 .

  • Ni Java ni C # n’ont techniquement besoin d’ une méthode principale. Eh bien, C # a besoin d’être compilé, mais pas même Java. Et dans aucun cas, il est nécessaire pour l'exécution. Cela ne semble donc pas être une restriction technique. Et, comme je l'ai mentionné dans le premier paragraphe, une simple convention semble étrangement incompatible avec le principe de conception général de Java et de C #.

Pour être clair, il n’ya pas d’ inconvénient particulier à avoir une mainméthode statique , c’est tout simplement étrange , ce qui m’a amené à me demander s’il existait une justification technique.

Je suis intéressé par une réponse définitive d'une source primaire ou secondaire, pas de simples spéculations.


1 Bien qu'il existe un callback ( Startup) qui peut l'intercepter.

Konrad Rudolph
la source
4
@mjfgates J'espérais aussi avoir bien précisé que ce n'est pas simplement «pourquoi les gens ne l'ont-ils pas fait comme je le veux» et que les raisons m'intéressent vraiment.
Konrad Rudolph
2
Pour Java, je pense que le raisonnement est simple: lors du développement de Java, ils savaient que la plupart des personnes apprenant le langage maîtriseraient le C / C ++ auparavant. Ainsi, non seulement Java ressemble beaucoup à C / C ++ au lieu de dire smalltalk, mais il a également repris les idiosynchronisations de C / C ++ (il suffit de penser aux littéraux entiers octaux). Comme c / c ++ utilisent tous deux une méthode principale, il était logique de procéder de la même manière pour Java de ce point de vue.
Voo
5
@ Jarrod Vous êtes injuste. Je pensais l'avoir fait très clairement pas dans une diatribe. “Pas constructif”? Comment? Je demande explicitement des références, pas seulement des discussions frénétiques. Vous êtes bien entendu libre de ne pas être d'accord sur le fait qu'il s'agit d'une question intéressante . Mais si ce genre de questions est OT ici, je ne vois vraiment pas à quoi sert Programmers.SE.
Konrad Rudolph
2
Méta discussion pertinente .
Yannis
3
Question: S'il s'agit d'un objet d'application, vous n'avez pas besoin de deux choses. 1) un constructeur. 2) Une méthode sur l'objet pour exécuter votre application. Le constructeur doit compléter pour que l'objet soit valide et donc utilisable.
Martin York

Réponses:

38

TL; DR

En Java, la raison public static void main(String[] args)est que

  1. Gosling voulait
  2. le code écrit par une personne expérimentée en C (pas en Java)
  3. être exécuté par une personne habituée à exécuter PostScript sur NeWS

http://i.stack.imgur.com/qcmzP.png

 
Pour C #, le raisonnement est transitoirement similaire pour ainsi dire. Les concepteurs de langage ont gardé la syntaxe de point d’entrée de programme familière pour les programmeurs venant de Java. Comme le dit l’architecte C #, Anders Hejlsberg ,

... notre approche avec C # a simplement été d'offrir une alternative ... aux programmeurs Java ...

 

Version longue

expansion au-dessus et soutenu avec des références ennuyeuses.

 

java Terminator Hasta la vista bébé!

VM Spec, 2.17.1 Démarrage de la machine virtuelle

... La manière dont la classe initiale est spécifiée sur la machine virtuelle Java dépasse le cadre de la présente spécification, mais il est typique, dans les environnements hôtes utilisant des lignes de commande, que le nom complet de la classe soit spécifié comme suit: un argument de ligne de commande et les arguments suivants à utiliser comme chaînes à fournir comme arguments de la méthode main. Par exemple, en utilisant le SDK Java 2 de Sun pour Solaris, la ligne de commande

java Terminator Hasta la vista Baby!

lancera une machine virtuelle Java en appelant la méthode main of class Terminator(une classe dans un paquet non nommé) et en lui transmettant un tableau contenant les quatre chaînes "Hasta", "la", "vista" et "Baby!" ...

... voir aussi: Annexe: J'ai besoin de tes vêtements, de tes bottes et de ta moto

  • Mon interprétation:
    exécution ciblée pour une utilisation similaire aux scripts classiques dans l'interface de ligne de commande.

 

marche de côté importante

... cela permet d'éviter quelques fausses traces dans notre enquête.

VM Spec, 1.2 La machine virtuelle Java

La machine virtuelle Java ne sait rien du langage de programmation Java ...

J'ai remarqué ci-dessus lors de l'étude du chapitre précédent - 1.1 Histoire qui, selon moi, pourrait être utile (mais s'est avérée inutile).

  • Mon interprétation: l'
    exécution est régie par la seule spécification de machine virtuelle, qui
    déclare explicitement qu'elle n'a rien à voir avec le langage Java
    => OK pour ignorer JLS et tout élément lié au langage Java.

 

Gosling: un compromis entre C et langage de script ...

Sur la base de ce qui précède, j'ai commencé à rechercher sur le Web l' historique de la JVM . N'a pas aidé, trop de déchets dans les résultats.

Ensuite, je me suis rappelé les légendes sur Gosling et ma recherche a été réduite à l’ histoire de Gosling JVM .

Eureka! Comment les spécifications de la JVM sont devenues

Dans cette allocution du JVM Languages ​​Summit 2008, James Gosling aborde ... la création de Java, ... un compromis entre le langage C et le langage de script ...

  • Mon interprétation:
    déclaration explicite qu’au moment de la création,
    C et le script ont été considérés comme les influences les plus importantes.
     
    Déjà vu clin d' œil à des scripts dans VM Spec 2.17.1, les
    arguments de ligne de commande expliquent suffisamment , String[] args
    mais staticet mainne sont pas encore là, le besoin de creuser plus loin ...

Notez que lorsque vous tapez ceci - connexion de C, de script et de VM Spec 1.2 avec sa technologie rien de Java - je me sens comme quelque chose de familier, quelque chose ... orienté objet est en train de disparaître lentement. Prends ma main et continue à avancer Ne ralentis pas, nous y sommes presque

Les diapositives Keynote sont disponibles en ligne: 20_Gosling_keynote.pdf , ce qui est très pratique pour copier des points clés.

    page 3

        La préhistoire de Java
        * Qu'est-ce qui a façonné ma pensée?

    page 9

        Nouvelles
        * Système de fenêtre extensible en réseau
        * Un système de fenêtre basé sur le script ....
          PostScript (!!)

    page 16

        Un grand (mais calme) objectif:
          À quel point pourrais-je me rapprocher d'un
          "script" sentir ...

    page 19

        Le concept original
        * Était tout au sujet de la construction
          réseaux de choses,
          orchestré par un script
          la langue
        * (Shells Unix, AppleScript, ...)

    page 20

        Un loup dans l'habillement du mouton
        * La syntaxe C pour rendre les développeurs
          confortable

A-ha! Regardons de plus près la syntaxe C .

L'exemple "bonjour, monde" ...

main()
{
    printf("hello, world\n");
}

... une fonction nommée main est en cours de définition. La fonction principale remplit une fonction particulière dans les programmes C; l'environnement d'exécution appelle la fonction principale pour commencer l'exécution du programme.

... La fonction principale a en fait deux arguments int argcet char *argv[], respectivement, qui peuvent être utilisés pour gérer des arguments en ligne de commande ...

Est-ce qu'on se rapproche? tu paries. Il est également intéressant de suivre le lien "principal" de la citation ci-dessus:

la fonction principale est l'endroit où un programme commence l'exécution. Il est responsable de l'organisation de haut niveau de la fonctionnalité du programme et a généralement accès aux arguments de commande donnés au programme lors de son exécution.

  • Mon interprétation:
    pour que le développeur C soit à l'aise, le point d'entrée du programme doit être main.
    De plus, comme Java requiert que toute méthode soit dans la classe, elle Class.mainest
    aussi proche que possible: invocation statique, juste nom de classe et point,
    pas de constructeur s'il vous plaît - C ne sait rien de tel.
     
    Ceci s’applique également de manière transitoire au C #, en tenant compte de
    l’idée d’une migration aisée de ce dernier vers Java.

Les lecteurs qui pensent que le point d’entrée du programme est sans importance sont invités à rechercher et à vérifier les questions de Stack Overflow dans lesquelles des personnes de Java SE essaient d’écrire Hello World pour Java ME MIDP. Remarque Le point d’entrée MIDP n’a pas de mainni static.

 

Conclusion

Sur la base de ce qui précède, je dirais que static, mainet que String[] argsc’était au moment de la création de Java et de C # le choix le plus raisonnable pour définir le point d’entrée du programme .

 

Annexe: J'ai besoin de tes vêtements, de tes bottes et de ta moto

Je dois admettre que la lecture de VM Spec 2.17.1 était très amusant.

... la ligne de commande

java Terminator Hasta la vista Baby!

lancera une machine virtuelle Java en appelant la méthode main of class Terminator(une classe dans un paquet non nommé) et en lui transmettant un tableau contenant les quatre chaînes "Hasta", "la", "vista" et "Baby!".

Nous décrivons maintenant les étapes que la machine virtuelle peut entreprendre Terminator, en tant qu’exemple des processus de chargement, de liaison et d’initialisation décrits plus en détail dans les sections suivantes.

La tentative initiale ... découvre que la classe Terminatorn'est pas chargée ...

Une fois Terminatorchargé, il doit être initialisé avant que main puisse être appelé et un type (classe ou interface) doit toujours être lié avant son initialisation. La liaison (§2.17.3) implique la vérification, la préparation et (facultativement) la résolution ...

Vérification (§2.17.3) vérifie que la représentation chargée de Terminatorest bien formée ...

La résolution (§2.17.3) est le processus de vérification des références symboliques de la classe Terminator...

 
Références symboliques de Terminatoroh ouais.

moucheron
la source
2
Pour une raison quelconque, j'ai eu du mal à croire que "modernité" était un mot réel.
Someguy
@Songo histoire de la réponse est aussi comme un film. Il a d'abord été posté sur meta , dans une discussion sur la clôture de la question: "Si la question était rouverte, j'écrirais probablement une réponse comme ci-dessous ..." Elle a ensuite été utilisée pour appuyer l'appel à la réouverture et finalement déplacée ici.
Gnat
16

Cela me semble vaguement abusif. Un constructeur est utilisé pour l'initialisation d'un objet: il configure un objet, qui est ensuite utilisé par le code qui l'a créé.

Si vous insérez des fonctionnalités d'utilisation de base dans le constructeur et n'utilisez jamais l'objet créé par le constructeur dans le code externe, vous enfreignez les principes de la POO. Fondamentalement, faire quelque chose de vraiment bizarre sans raison apparente.

Pourquoi voudriez-vous faire ça quand même?

Maçon Wheeler
la source
5
Mais l'instance d'application n'est-elle pas logiquement un objet? Pourquoi cela serait-il abusif? Quant à l’utilisation de l’objet, il a un seul objectif: représenter l’application en cours d’exécution. Cela semble très SoC -y pour moi. "Pourquoi voudriez-vous faire cela?" - Je m'intéresse simplement à la raison d'être de la décision, car je la trouve en contradiction avec le reste de la mentalité.
Konrad Rudolph
7
@ KonradRudolph: un constructeur, comme un acquéreur de propriété, est généralement censé se terminer dans un délai imparti sans attendre qu'un événement asynchrone (comme une entrée utilisateur) se produise. Il serait possible d'avoir un constructeur qui lance un thread d'application principal, mais cela ajoute un niveau de complexité qui peut ne pas être nécessaire pour toutes les applications. Exiger qu'une application de la console qui imprime simplement "Hello world" sur la sortie standard devrait générer un thread supplémentaire serait maladroit. L'utilisation d'une Mainméthode fonctionne bien pour le cas simple, et n'est pas vraiment un problème dans les cas les plus difficiles, alors pourquoi pas?
Supercat
9

Pour Java, je penserais que le raisonnement est simple: lors du développement de Java, les développeurs savaient que la plupart des personnes apprenant le langage maîtriseraient le C / C ++ auparavant.

Ainsi, non seulement Java ressemble beaucoup à C / C ++ au lieu de dire smalltalk, mais il a également repris les idiosynchronisations de C / C ++ (il suffit de penser aux littéraux entiers octaux). Comme c / c ++ utilisent tous deux une méthode principale, il était logique de procéder de la même manière pour Java de ce point de vue.

Je suis sûr que je me souviens de bloch ou de quelqu'un qui a dit quelque chose dans ce sens à propos de la raison pour laquelle ils ont ajouté des littéraux octaux entiers, je vais voir si je peux trouver des sources :)

Voo
la source
2
Si le même aspect que le C ++ était si important pour Java, pourquoi ont-ils par exemple changé :en extends? Et à l' public static void main(String [ ] args)intérieur d'une classe, c'est très différent de l' int main(int argc, char **argv)extérieur.
svick
2
@svick Une possibilité: Java a introduit les interfaces et il était clair qu'ils souhaitaient séparer les deux concepts (interfaces / classes héritées) - avec un seul "mot clé" qui ne fonctionnerait pas. Et "tout à fait différent"? C'est le mappage le plus proche possible et, jusqu'à présent, je n'ai jamais vu un programmeur c ++ avoir du mal à comprendre que la méthode principale statique soit le point d'entrée. Contrairement à cela, avoir une classe appelée Application ou quelque chose dont le constructeur est utilisé est quelque chose qui pourrait paraître étrange à la plupart des programmeurs c ++.
Voo
@svick int dans c pour annuler en java était lié à la manière dont le code de retour d'une application était généré - en java, il valait 0 sauf si System.exit (int) est appelé. Le changement de paramètres dépend de la manière dont les tableaux de chaînes sont passés dans chaque langue. Tout en Java est dans une classe - il n'y a aucune option pour l'avoir ailleurs. Changer :pour extendsest une question de syntaxe et sont essentiellement les mêmes. Tout le reste est dicté par la langue.
@MichaelT Mais toutes ces décisions sont des décisions de conception qui rendent Java différent de C ++. Alors, pourquoi garder Java identique à C ++ serait important dans le cas de main(), alors qu’il n’était apparemment pas assez important dans d’autres cas.
svick
@svick Sauf qu'il est parfaitement correct de ne rien renvoyer de main en C aussi et que de telles trivialités ne risquent de dérouter personne de toute façon. Le but n'était pas de recréer le c ++ et toutes ses erreurs, mais seulement de rendre le programmeur plus à l'aise. Que pensez-vous qu'un programmeur C ++ aura une lecture plus facile: code Java ou objective-c? Selon vous, qu'est-ce qui semblera plus évident pour un programmeur C ++, une méthode principale ou un constructeur d'une classe en tant que point d'entrée?
Voo
6

Eh bien, il existe de nombreuses fonctions principales qui exécutent une boucle infinie. Un constructeur qui travaille de cette façon (avec un objet qui ne se construit jamais) est ce qui me semble étrange.

Il y a tellement de choses amusantes à propos de ce concept. Votre logique s'exécute sur un objet à naître, des objets nés pour mourir (puisqu'ils font tout leur travail dans le constructeur), ...

Tous ces effets secondaires ne vont-ils pas corrompre beaucoup plus le wagon OO qu'un simple public (parce qu'il doit être accédé par un inconnu) statique (car aucune instance n'est nécessaire pour que nous puissions commencer) void main (parce que c'est le point d'entrée )?

Pour qu'un point d'entrée simple, simple et fonctionnel, existe en Java, il faut automatiquement public et static. Bien qu’il s’agisse d’une méthode statique , elle se résume à ce que nous pouvons obtenir de plus près d’une fonction simple pour accomplir ce que nous souhaitons: un simple point d’entrée.

Si vous n'allez pas adopter un point d'entrée simple, clair et fonctionnel, comme point d'entrée. Quelle est la suite qui ne semble pas étrange en tant que constructeur que ce n'est pas destiné à construire?

pepper_chico
la source
1
Je dirais que le problème n'était pas d'avoir des fonctions de première classe. Coller la main () dans un objet (qui n'est pas instancié avant que l'appel ne soit appelé) est un peu anti-modèle. Peut-être qu’ils devraient avoir un objet "application" qui est construit et exécute sa méthode main () non statique, vous pouvez alors mettre l’initialisation au démarrage dans le constructeur. = niveau main () fn serait bien aussi. La statique principale est un peu un kludge dans l'ensemble.
gbjbaanb
3

Vous pouvez rapidement exécuter des tests autonomes sur une classe, pendant le développement, en collant un main()dans la classe que vous essayez de tester.

Graham Borland
la source
1
Ceci est pour moi probablement la raison la plus convaincante, car cela permet également de multiples points d'entrée pendant le développement pour tester différentes configurations, etc.
cgull
0

Vous devez commencer quelque part. Un environnement statique principal est l’environnement d’exécution le plus simple qui soit. Aucune instance n’est nécessaire (en dehors de la JVM et des paramètres de chaîne simples) ne doit être créée. Elle peut donc "apparaître" avec un minimum de tracas (et une faible probabilité d'une erreur de codage empêchant le démarrage, etc.) et permet de faire des choses simples sans beaucoup d'autres configurations.

Fondamentalement une application de KISS.

[Et, bien sûr, la raison principale est: pourquoi pas?]

Daniel R Hicks
la source
3
Comme je l'ai dit, cela ne me convainc pas du tout. Les objets ne s'instanciées avant, et le code est exécuté avant. Il faudrait une citation de l’un des développeurs originaux pour me convaincre que c’était la raison.
Konrad Rudolph
2
La quantité de travail nécessaire pour instancier une classe à partir de code C est à peu près identique à l'appel d'une méthode statique. Vous devez même effectuer les mêmes vérifications (la classe existe-t-elle? Très bien, a-t-elle un constructeur public avec la bonne signature? bien alors allez-y).
Voo
Aucun objet utilisateur ne doit être créé. Le constructeur d'objet n'est pas exécuté. L'API est extrêmement simple. Et c'est le plus facile à comprendre.
Daniel R Hicks
0

À ma connaissance, la raison principale est simple. Sun était une société Unix vendant des machines Unix et Unix est l’objet de la convention C "principale (args)" pour invoquer un binaire .

De plus, Java a été explicitement conçu pour être facilement lisible par les programmeurs C et C ++. Il n’y avait donc aucune bonne raison de ne pas simplement se contenter de la convention C.

L'approche choisie, dans laquelle chaque classe peut avoir une méthode d'appel, est assez flexible, en particulier en combinaison avec la Main-Classligne du fichier MANIFEST.MF dans un fichier jar exécutable.

moucheron
la source
Bien sûr, le fichier jar n’a été inventé que bien plus tard.
Daniel R Hicks
-1

Selon la philosophie de la programmation orientée objet, un programme ne serait pas un objet du point de vue du processus de système d’exploitation, car il n’était pas possible d’en avoir plus d’un par définition.

De plus, un constructeur n'est en aucun cas un point d'entrée.

Il me semble que le choix le plus raisonnable est d’avoir main comme fonction statique, ce qu’il est en réalité à la fin de la journée. Compte tenu de l’architecture de machines virtuelles telles que JVM et CLR, tout autre choix le pousserait inutilement.

Yam Marcovic
la source
1
Je pense que vous avez tort là-bas. Il est possible d'avoir plus d'un processus, donc plus d'un objet. Incidemment, cela équivaut entièrement à instancier des Runnableobjets à plusieurs threads.
Konrad Rudolph
Un processus est un programme en cours d'exécution et vous ne pouvez démarrer un processus qu'une seule fois via un point d'entrée. Les threads ont leurs propres points d'entrée mais sont toujours dans le même processus.
Yam Marcovic
1
Comme je l'ai dit ci-dessous, la réponse de Someguy n'est pas pertinente. Ce qui est important, c'est la cohérence logique . Logiquement, les processus sont représentés sous forme d'objets par le programme de lancement (système d'exploitation, machine virtuelle, etc.) et sont initialisés.
Konrad Rudolph
@ KonradRudolph C'est vrai, mais l'initialisation d'un programme n'est qu'une partie de l'initialisation d'un processus et ne légitime pas un constructeur de programme.
Yam Marcovic