Почему статический метод main в Java и C #, а не конструктор?

54

Я ищу точный ответ из первичного или вторичного источника, почему (в частности) Java и C # решили использовать статический метод в качестве своей точки входа, а не представлять экземпляр приложения экземпляром Applicationкласса (с точкой входа быть подходящим конструктором).


Предпосылки и детали моего предыдущего исследования

Об этом уже спрашивали. К сожалению, существующие ответы просто напрашиваются на вопрос . В частности, следующие ответы меня не удовлетворяют, так как я считаю их неверными:

  • Не было бы двусмысленности, если бы конструктор был перегружен. - На самом деле, C # (а также C и C ++) допускают разные сигнатуры, Mainпоэтому существует и существует одна и та же потенциальная неоднозначность.
  • staticМетод означает объекты не могут быть созданы таким образом , прежде чем порядок инициализации ясен. - Это просто фактически неверно, некоторые объекты являются экземплярами ранее (например , в статическом конструкторе).
  • Таким образом, они могут быть вызваны средой выполнения без необходимости создания экземпляра родительского объекта. - Это не ответ вообще.

Просто чтобы объяснить, почему я считаю этот вопрос актуальным и интересным:

  • Многие структуры делают использовать классы для представления приложений и конструкторов в качестве точек входа. Например, прикладная среда VB.NET использует выделенный главный диалог (и его конструктор) в качестве точки входа 1 .

  • Ни Java, ни C # технически не нуждаются в основном методе. Ну, C # нужен для компиляции, но Java даже не это. И ни в том, ни в другом случае это не нужно для исполнения. Так что это не является техническим ограничением. И, как я уже упоминал в первом абзаце, для простого соглашения это кажется странно несовместимым с общим принципом разработки Java и C #.

Чтобы быть ясным, нет никакого особенного недостатка в наличии статического mainметода, это просто явно странно , что заставило меня задуматься, есть ли какое-то техническое обоснование этого.

Я заинтересован в окончательном ответе из первичного или вторичного источника, а не просто в предположениях.


1 Хотя есть обратный вызов ( Startup), который может перехватить это.

Конрад Рудольф
источник
4
@mjfgates Кроме того, я надеялся прояснить, что это не просто «почему люди не делают это так, как я хочу», и что я действительно заинтересован в причинах.
Конрад Рудольф
2
Что касается Java, я думаю, что причина проста: при разработке Java они знали, что большинство людей, изучающих язык, будут знать C / C ++ заранее. Следовательно, Java не только во многом похожа на C / C ++, а не на smalltalk, но и переняла отличительные черты от C / C ++ (просто подумайте о восьмеричных целочисленных литералах). Поскольку c / c ++ оба используют основной метод, то же самое для java имеет смысл с этой точки зрения.
Во
5
@ Джаррод Ты несправедлив. Я думал, что сделал это совершенно ясно, не напрасно. «Не конструктивно»? Как так? Я явно прошу ссылки, а не просто дикие обсуждения. Вы, конечно, можете не согласиться с тем, что это интересный вопрос. Но если такого рода вопросы здесь ОТ, я действительно не понимаю, для чего предназначен Programmers.SE.
Конрад Рудольф
2
Соответствующая мета-дискуссия .
Яннис
3
Вопрос: Если это объект приложения, вам не нужны две вещи. 1) Конструктор. 2) Метод объекта для запуска вашего приложения. Конструктор должен завершиться, чтобы объект был действительным и, следовательно, работоспособным.
Мартин Йорк,

Ответы:

38

TL; DR

В Java причина в public static void main(String[] args)том, что

  1. Гослинг хотел
  2. код, написанный кем-то опытным в C (не в Java)
  3. быть выполненным кем-то, кто привык запускать PostScript в NeWS

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

 
Для C # рассуждения транзитивно похожи, так сказать. Разработчики языка сохранили синтаксис точки входа в программу знакомым для программистов из Java. Как говорит C # архитектор Андерс Хейлсберг ,

... наш подход к C # просто предлагал альтернативу ... программистам на Java ...

 

Длинная версия

расширение выше и подкреплено скучными ссылками.

 

Java Терминатор Хаста ла Виста Детка!

VM Spec, 2.17.1 Запуск виртуальной машины

... Способ, которым начальный класс указывается для виртуальной машины Java, выходит за рамки данной спецификации, но для сред хоста, использующих командные строки, типично, чтобы полное имя класса указывалось как аргумент командной строки и для последующих аргументов командной строки, которые будут использоваться в качестве строк, которые будут предоставлены в качестве аргумента метода main. Например, используя Sun Java 2 SDK для Solaris, командную строку

java Terminator Hasta la vista Baby!

запустит виртуальную машину Java, вызвав метод main класса Terminator(класс в неназванном пакете) и передав ему массив, содержащий четыре строки: «Hasta», «la», «vista» и «Baby!» ...

... см. также: Приложение: мне нужна твоя одежда, твои ботинки и твой мотоцикл

  • Моя интерпретация:
    выполнение предназначено для использования как типичные сценарии в интерфейсе командной строки.

 

важный шаг

... это помогает избежать нескольких ложных следов в нашем расследовании.

VM Spec, 1.2 Виртуальная машина Java

Виртуальная машина Java ничего не знает о языке программирования Java ...

Я заметил выше, когда изучал предыдущую главу - 1.1 История, которая, на мой взгляд, могла бы быть полезной (но оказалась бесполезной).

  • Моя интерпретация:
    выполнение регулируется только спецификацией VM, которая
    явно заявляет, что она не имеет ничего общего с языком Java
    => OK, чтобы игнорировать JLS и все, что связано с языком Java.

 

Гослинг: компромисс между Си и языком сценариев ...

Исходя из вышеизложенного, я начал искать в Интернете историю JVM . Не помогло, слишком много мусора в результатах.

Затем я вспомнил легенды о Гослинге и сузил свой поиск до истории Гослинг-JVM .

Эврика! Как появилась спецификация JVM

В этом лейтмотиве JVM Languages ​​Summit 2008 Джеймс Гослинг обсуждает ... создание Java, ... компромисс между C и языком сценариев ...

  • Моя интерпретация:
    явное заявление о том, что в момент создания
    C и сценарии считались наиболее важными факторами.
     
    Уже видели кивок к скриптинг в VM Spec 2.17.1,
    аргументы командной строки , достаточно объяснить , String[] args
    но staticи mainеще не там, нужно копать дальше ...

Обратите внимание, что при наборе этого слова - соединяя C, скрипты и VM Spec 1.2 с его ничего-Java-я - я чувствую себя как что-то знакомое, что-то ... объектно-ориентированное медленно исчезает. Возьми меня за руку и продолжай двигаться.

Слайды Keynote доступны онлайн: 20_Gosling_keynote.pdf , довольно удобно для копирования ключевых моментов.

    страница 3

        Предыстория Java
        * Что сформировало мое мышление

    страница 9

        Новости
        * Сетевая расширяемая оконная система
        * Оконная система на основе сценариев ....
          PostScript (!!)

    страница 16

        Большая (но тихая) цель:
          Как близко я мог добраться до
          "сценарий" чувствую ...

    страница 19

        Оригинальная концепция
        * Было все о строительстве
          сети вещей,
          организован с помощью сценариев
          язык
        * (Оболочки Unix, AppleScript, ...)

    страница 20

        Волк в овечьей шкуре
        * Синтаксис C, чтобы сделать разработчиков
          удобный

Ага! Давайте подробнее рассмотрим синтаксис Си .

Пример "Привет, мир" ...

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

... определяется функция с именем main. Основная функция служит специальное назначение в программах C; среда выполнения вызывает основную функцию, чтобы начать выполнение программы.

... Функция main на самом деле имеет два аргумента int argcи char *argv[], соответственно, которые могут использоваться для обработки аргументов командной строки ...

Мы приближаемся? Вы держите пари Также стоит перейти по «главной» ссылке сверху цитаты:

основная функция - то, где программа начинает выполнение. Он отвечает за высокоуровневую организацию функциональных возможностей программы и, как правило, имеет доступ к аргументам команды, переданным программе при ее выполнении.

  • Моя интерпретация:
    чтобы быть удобным для разработчика на C, должна быть точка входа в программу main.
    Кроме того, поскольку Java требует, чтобы любой метод был в классе, Class.mainон
    настолько близок, насколько это возможно: статический вызов, просто имя класса и точка,
    пожалуйста , никаких конструкторов - C ничего такого не знает.
     
    Это также транзитивно относится к C #, принимая во внимание
    идею легкого перехода на него с Java.

Читатели, считающие, что знакомая точка входа в программу не имеет значения, приглашаются на поиск и проверку вопросов переполнения стека, где ребята из Java SE пытаются написать Hello World для Java ME MIDP. Примечание. Точка входа MIDP не имеет mainни static.

 

Заключение

На основании выше , я бы сказал , что static, mainи String[] argsбыли в моменты Java и C # создание наиболее разумных решений по определению программы точки входа .

 

Приложение: мне нужна твоя одежда, твои ботинки и твой мотоцикл

Должен признаться, чтение VM Spec 2.17.1 было огромным удовольствием.

... командная строка

java Terminator Hasta la vista Baby!

запустит виртуальную машину Java, вызвав метод main класса Terminator(класс в неназванном пакете) и передав ему массив, содержащий четыре строки: «Hasta», «la», «vista» и «Baby!».

Теперь мы опишем шаги, которые может предпринять виртуальная машина Terminator, в качестве примера процессов загрузки, компоновки и инициализации, которые описаны далее в последующих разделах.

Первоначальная попытка ... обнаруживает, что класс Terminatorне загружен ...

После Terminatorзагрузки он должен быть инициализирован перед вызовом main, а тип (класс или интерфейс) всегда должен быть связан до его инициализации. Связывание (§2.17.3) включает проверку, подготовку и (необязательно) разрешение ...

Проверка (§2.17.3) проверяет, что загруженное представление Terminatorправильно сформировано ...

Резолюция (§2.17.3) - это процесс проверки символических ссылок из класса Terminator...

 
Символические ссылки от Terminatorах да.

комар
источник
2
По некоторым причинам мне было трудно поверить, что слово «современность» было настоящим словом.
Someguy
@ Сонго история ответа также похожа на фильм. Впервые он был опубликован в meta , в ходе обсуждения вопроса о закрытии вопроса: «Если вопрос будет вновь открыт, я, вероятно, напишу ответ, подобный приведенному ниже ...» Затем он был использован для подтверждения апелляции на повторное открытие и, наконец, перенесен сюда
комнат
16

Это просто кажется мне оскорбительным. Конструктор используется для инициализации объекта: он устанавливает объект, который затем используется кодом, который его создал.

Если вы помещаете базовые функциональные возможности использования в конструктор, а затем никогда не используете объект, который конструктор создает во внешнем коде, то вы нарушаете принципы ООП. По сути, делать что-то действительно странное без видимой причины.

Зачем тебе это делать?

Мейсон Уилер
источник
5
Но не является ли «экземпляр приложения» логически объектом? Почему это было бы оскорбительно? Что касается использования объекта - он имеет одну цель: представлять запущенное приложение. Звуки очень SoC -y мне. «Зачем вам это делать?» - я просто заинтересован в обосновании этого решения, поскольку нахожу его в противоречии с остальным менталитетом.
Конрад Рудольф
7
@KonradRudolph: обычно ожидается, что конструктор, как и средство получения свойств, завершится за ограниченное время, не дожидаясь появления какого-либо асинхронного события (например, пользовательского ввода). Можно было бы иметь конструктор, который запускает основной поток приложения, но при этом добавляется уровень сложности, который может понадобиться не для всех приложений. Требование, чтобы консольное приложение, которое просто печатает «Hello world» в стандартный вывод, должно порождать дополнительный поток, было бы глупо. Использование Mainметода хорошо работает в простом случае и не является проблемой в более сложных случаях, так почему бы и нет?
суперкат
9

Что касается Java, я думаю, что причина проста: при разработке Java разработчики знали, что большинство людей, изучающих язык, будут знать C / C ++ заранее.

Следовательно, Java не только во многом похожа на C / C ++, а не на smalltalk, но и переняла отличительные черты от C / C ++ (просто подумайте о восьмеричных целочисленных литералах). Поскольку c / c ++ оба используют основной метод, то же самое для java имеет смысл с этой точки зрения.

Я почти уверен, что помню, как Блох или кто-то говорил что-то вроде этого о том, почему они добавили восьмеричные целочисленные литералы, я посмотрю, смогу ли я найти некоторые источники :)

Voo
источник
2
Если выглядело так же, как C ++, было так важно для Java, почему они, например, изменились :на extends? И public static void main(String [ ] args)внутри класса это совсем не то, что int main(int argc, char **argv)вне класса.
свик
2
@svick Одна возможность: Java представила интерфейсы, и они явно хотели разделить две концепции (наследуя интерфейсы / классы) - с помощью только одного «ключевого слова», которое не будет работать. И "совсем другое"? Это ближайшая возможная карта, и до сих пор я никогда не видел, чтобы у программиста на С ++ возникали проблемы с пониманием того, что метод static main является точкой входа. В отличие от того, что наличие класса с именем Application или чего-то, чей конструктор используется, это то, что выглядело бы странно для большинства программистов на c ++.
Во
@svick int в c, чтобы void в java, был связан с тем, как был сгенерирован код возврата из приложения - в java его 0, если не вызывается System.exit (int). Изменение параметров связано с тем, как массивы строк передаются на каждом языке. Все в Java находится в классе - нет возможности иметь это в другом месте. Изменение :на extendsэто вопрос синтаксиса и по сути то же самое. Все остальное продиктовано языком.
@MichaelT Но все это дизайнерские решения, которые отличают Java от C ++. Так почему важно сохранять Java таким же, как C ++, в случае main(), когда, очевидно, это было недостаточно важно в других случаях.
свик
@svick За исключением того, что вполне нормально не возвращать что-либо из main в C тоже, и такие мелочи вряд ли кого-нибудь смущают. Суть не в том, чтобы воссоздать c ++ и все его ошибки, а лишь в том, чтобы сделать программиста больше дома. Как вы думаете, что программисту на C ++ будет легче читать: Java или код Objective-C? Как вы думаете, что будет более очевидным для программиста на C ++ основной метод или конструктор некоторого класса в качестве точки входа?
Во
6

Ну, есть много основных функций, которые просто запускают бесконечный цикл. Конструктор, работающий таким образом (с объектом, который никогда не создается) - это то, что мне кажется странным.

В этой концепции так много забавных вещей. Ваша логика работает поверх нерожденного объекта, объектов, которые рождены, чтобы умереть (поскольку они делают всю свою работу в конструкторе), ...

Разве все эти побочные эффекты не повредят OO wagon намного больше, чем простой общедоступный (потому что к нему должен получить доступ неизвестный) static (потому что нам не нужен экземпляр, чтобы начать работу) void main (потому что это точка входа )?

Для простой, простой, точки входа в функцию, существующей в Java, автоматически требуются public и static. Хотя это статический метод , он сводится к тому, что мы можем приблизить к простой функции для достижения желаемого: простой точке входа.

Если вы не собираетесь использовать простую, простую, функциональную точку входа в качестве точки входа. Что дальше, что не кажется странным как конструктор, который не предназначен для построения?

pepper_chico
источник
1
Я бы сказал, что проблема не в том, чтобы иметь первоклассные функции. Вставить main () внутри объекта (который не был создан до вызова main) - это немного против паттерна. Может быть, у них должен быть объект «application», который создается и запускает его нестатический метод main (), тогда вы можете поместить инициализацию запуска в конструктор, и это будет чувствовать себя намного лучше, чем использование статических методов, хотя простой top- = level main () fn тоже будет хорошо. Static main - это всего лишь клудж в целом.
gbjbaanb
3

Вы можете быстро запустить некоторые автономные тесты для класса во время разработки, вставив main()в класс, который вы пытаетесь протестировать.

Грэм Борланд
источник
1
Для меня это, пожалуй, самая веская причина, поскольку она также позволяет использовать несколько точек входа во время разработки для тестирования различных конфигураций и т. Д.
cgull
0

Вы должны начать где-нибудь. Статическое main - это простейшая среда выполнения, которую вы можете иметь - не нужно создавать ни одного экземпляра (вне JVM и простых строковых параметров), поэтому он может «придумать» минимум суеты (и малую вероятность) ошибки кодирования, препятствующей запуску и т. д.), и может делать простые вещи без множества других настроек.

В основном приложение KISS.

[И, конечно же, главная причина: почему бы и нет?]

Даниэль Р Хикс
источник
3
Как я уже сказал, меня это совсем не убеждает. Объекты действительно создаются раньше, а код выполняется раньше. Требуется цитата одного из разработчиков, чтобы убедить меня, что это и есть причина.
Конрад Рудольф
2
Объем работы, необходимый для создания экземпляра класса из кода C, практически идентичен вызову статического метода. Вы даже должны выполнять те же проверки (существует ли класс? Хорошо, есть ли у него открытый конструктор с правильной сигнатурой? хорошо, тогда вперед).
Во
Нет пользователь нуждается объект не будет создан. Конструктор объекта не выполняется. API-интерфейс невероятно прост. И это проще всего понять.
Даниэль Р Хикс
0

Насколько я понимаю, основная причина проста. Sun была Unix-компанией, продающей Unix-машины, и Unix - это то, для чего было разработано соглашение C "main (args)" для вызова двоичного файла.

Кроме того, Java была явно разработана так, чтобы ее было легко подобрать для программистов на C и C ++, поэтому не было веских оснований для того, чтобы просто не использовать соглашение C.

Выбранный подход, при котором каждый класс может иметь метод вызова, достаточно гибок, особенно в сочетании со Main-Classстрокой в ​​файле MANIFEST.MF в исполняемом фляге.

комар
источник
Конечно, файл с флягой не был изобретен намного позже.
Даниэль Р Хикс
-1

Это не согласуется с философией ООП, согласно которой программа была бы объектом с точки зрения процесса ОС, поскольку не существует способа иметь более одного по определению.

Кроме того, конструктор никоим образом не является точкой входа.

Мне кажется, что самый разумный выбор - использовать main как статическую функцию, какой она на самом деле является в конце дня. Учитывая архитектуру виртуальных машин, таких как JVM и CLR, любой другой выбор был бы излишним.

Ям Маркович
источник
1
Я думаю, что ты не прав там. Это есть возможность иметь более чем один процесс, следовательно , более одного объекта. Между прочим, это полностью эквивалентно созданию Runnableобъектов с несколькими потоками.
Конрад Рудольф
Процесс - это запущенная программа, и вы можете запустить процесс только один раз через одну точку входа. Потоки имеют свои собственные точки входа, но все еще находятся в том же процессе.
Ям Маркович
1
Как я уже сказал ниже, ответ какого-то парня не имеет значения. Что важно, так это логическая последовательность. Логически процессы представляются в виде объектов средством запуска (ОС, JVM и т. Д.) И инициализируются.
Конрад Рудольф
@KonradRudolph Верно, но инициализация программы - это только одна часть инициализации процесса, и она не узаконивает конструктор программы.
Ям Маркович