Por que um método principal estático em Java e C #, em vez de um construtor?


54

Estou procurando uma resposta definitiva de uma fonte primária ou secundária para saber por que (principalmente) Java e C # decidiram ter um método estático como ponto de entrada, em vez de representar uma instância de aplicativo por uma instância de uma Applicationclasse (com o ponto de entrada sendo um construtor apropriado).


Antecedentes e detalhes da minha pesquisa anterior

Isso já foi perguntado antes. Infelizmente, as respostas existentes estão apenas implorando a pergunta . Em particular, as seguintes respostas não me satisfazem, pois as considero incorretas:

  • Haveria ambiguidade se o construtor estivesse sobrecarregado. - De fato, o C # (assim como o C e o C ++) permite assinaturas diferentes para Mainque exista a mesma ambiguidade potencial e seja tratada.
  • Um staticmétodo significa que nenhum objeto pode ser instanciado antes, para que a ordem de inicialização seja clara. - Isso é realmente errado, alguns objetos são instanciados antes (por exemplo, em um construtor estático).
  • Portanto, eles podem ser chamados pelo tempo de execução sem precisar instanciar um objeto pai. - Isso não é resposta.

Apenas para justificar ainda mais por que acho que essa é uma pergunta válida e interessante:

  • Muitas estruturas de fazer usar classes para representar aplicativos e construtores como pontos de entrada. Por exemplo, a estrutura do aplicativo VB.NET usa um diálogo principal dedicado (e seu construtor) como ponto de entrada 1 .

  • Nem Java nem C # tecnicamente precisam de um método principal. Bem, o C # precisa de um para compilar, mas o Java nem isso. E em nenhum dos casos é necessário para execução. Portanto, isso não parece ser uma restrição técnica. E, como mencionei no primeiro parágrafo, para uma mera convenção, parece estranhamente inadequado com o princípio geral de design de Java e C #.

Para ser claro, não há uma desvantagem específica em ter um mainmétodo estático , é apenas distintamente estranho , o que me fez pensar se havia alguma lógica técnica por trás dele.

Estou interessado em uma resposta definitiva de uma fonte primária ou secundária, não em meras especulações.


1 Embora exista um retorno de chamada ( Startup) que possa interceptar isso.


4
@mjfgates Além disso, eu esperava ter deixado claro que isso não é simplesmente "por que as pessoas não fizeram do jeito que eu quero" e que estou realmente interessado nos motivos.
21911 Konrad Rudolph

2
Para Java, acho que o raciocínio é simples: ao desenvolver Java, eles sabiam que a maioria das pessoas que aprendia a linguagem conheceria C / C ++ de antemão. Portanto, o Java não apenas se parece muito com o C / C ++ em vez de dizer smalltalk, mas também assumiu as idiossincrasias do C / C ++ (pense apenas em literais inteiros octais). Como o c / c ++ usa um método principal, fazer o mesmo para java fazia sentido desse ponto de vista.
Voo

5
@Jarrod Você é injusto. Eu pensei que tinha feito isso claramente não em um discurso retórico. "Não construtivo"? Como assim? Estou explicitamente pedindo referências, não apenas discussões malucas. É claro que você pode discordar de que essa é uma pergunta interessante . Mas se esse tipo de pergunta é OT aqui, eu realmente não consigo ver para que finalidade o Programmers.SE serve.
21430 Konrad Rudolph


3
Pergunta: Se for um objeto de aplicativo, você não precisa de duas coisas. 1) Um construtor. 2) Um método no objeto para executar seu aplicativo. O construtor deve concluir para que o objeto seja válido e, portanto, executável.
Martin York

Respostas:


38

TL; DR

Em Java, o motivo public static void main(String[] args)é que

  1. Gosling queria
  2. o código escrito por alguém com experiência em C (não em Java)
  3. para ser executado por alguém acostumado a executar o PostScript no NeWS

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

 
Para C #, o raciocínio é transitivamente semelhante, por assim dizer. Os designers de linguagem mantinham a sintaxe do ponto de entrada do programa familiar para programadores vindos de Java. Como o arquiteto de C # Anders Hejlsberg coloca ,

... nossa abordagem com C # foi simplesmente oferecer uma alternativa ... aos programadores Java ...

 

Versão longa

expandindo acima e apoiado com referências chatas.

 

java Terminator Tem que ver Baby!

Especificações da VM, 2.17.1 Inicialização da máquina virtual

... A maneira como a classe inicial é especificada para a máquina virtual Java está além do escopo desta especificação, mas é típico, em ambientes host que usam linhas de comando, que o nome completo da classe seja especificado como um argumento da linha de comando e para que os argumentos subsequentes da linha de comando sejam usados ​​como seqüências de caracteres, a serem fornecidos como o argumento para o método principal. Por exemplo, usando o Java 2 SDK da Sun para Solaris, a linha de comando

java Terminator Hasta la vista Baby!

iniciará uma máquina virtual Java chamando o método main da classe Terminator(uma classe em um pacote sem nome) e transmitindo a ele uma matriz contendo as quatro strings "Hasta", "la", "vista" e "Baby!" ...

... veja também: Apêndice: Preciso de suas roupas, suas botas e sua motocicleta

  • Minha interpretação:
    execução direcionada para uso como scripts típicos na interface da linha de comandos.

 

passo importante

... isso ajuda a evitar alguns traços falsos em nossa investigação.

Especificações da VM, 1.2 A Java Virtual Machine

A máquina virtual Java não conhece nada da linguagem de programação Java ...

Notei acima ao estudar o capítulo anterior - 1.1 História, que pensei que poderia ser útil (mas acabou sendo inútil).

  • Minha interpretação: a
    execução é governada apenas pelas especificações da VM, que
    declara explicitamente que não tem nada a ver com a linguagem Java
    => OK para ignorar o JLS e qualquer coisa relacionada à linguagem Java.

 

Gosling: um compromisso entre C e linguagem de script ...

Com base no exposto, comecei a pesquisar na web o histórico da JVM . Não ajudou, muito lixo nos resultados.

Então, lembrei de lendas sobre Gosling e reduzi minha pesquisa à história da JVM de Gosling .

Eureka! Como a especificação da JVM veio a ser

Nesta palestra do JVM Languages ​​Summit 2008, James Gosling discute ... a criação de Java, ... um compromisso entre C e linguagem de script ...

  • Minha interpretação:
    declaração explícita de que, no momento da criação,
    C e scripts foram considerados as influências mais importantes.
     
    Já visto aceno para scripting em VM Spec 2.17.1,
    argumentos de linha de comando suficientemente explicar String[] args
    , mas statice mainnão estão lá ainda, necessidade de cavar ainda mais ...

Observe que, ao digitar isso - conectando C, scripts e VM Spec 1.2 com seu nada de Java -, sinto-me algo familiar, algo ... orientado a objetos está lentamente desaparecendo. Pegue minha mão e continue andando Não diminua a velocidade, estamos quase lá agora

Os slides do Keynote estão disponíveis on-line: 20_Gosling_keynote.pdf , bastante conveniente para copiar pontos-chave.

    página 3

        A pré-história do Java
        * O que moldou meu pensamento

    página 9

        Notícia
        * Sistema de janela extensível em rede
        * Um sistema de janelas baseado em scripts ....
          PostScript (!!)

    página 16

        Um objetivo grande (mas silencioso):
          Quão perto eu poderia chegar de um
          sensação de "script" ...

    página 19

        O conceito original
        * Era tudo sobre construção
          redes de coisas,
          orquestrado por um script
          língua
        * (Shell Unix, AppleScript, ...)

    página 20

        Um lobo em pele de cordeiro
        Sintaxe C para tornar os desenvolvedores
          confortável

A-ha! Vamos olhar mais de perto a sintaxe C .

O exemplo "olá, mundo" ...

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

... uma função chamada main está sendo definida. A função principal serve a um propósito especial em programas C; o ambiente de tempo de execução chama a função principal para iniciar a execução do programa.

... A função principal, na verdade, tem dois argumentos int argce char *argv[], respectivamente, que podem ser usados ​​para manipular argumentos de linha de comando ...

Estamos chegando mais perto? pode apostar. Também vale a pena seguir o link "principal" da citação acima:

a principal função é onde um programa inicia a execução. Ele é responsável pela organização de alto nível da funcionalidade do programa e geralmente tem acesso aos argumentos de comando fornecidos ao programa quando ele foi executado.

  • Minha interpretação:
    Para ser confortável para o desenvolvedor C, o ponto de entrada do programa deve ser main.
    Além disso, como o Java exige que qualquer método esteja na classe, Class.mainé o
    mais próximo possível: invocação estática, apenas nome e ponto da classe,
    sem construtores, por favor - C não sabe nada disso.
     
    Isso também se aplica transitivamente ao C #, levando em consideração
    a idéia de fácil migração para ele do Java.

Os leitores que pensam que o ponto de entrada do programa familiar não importa são convidados a pesquisar e verificar as perguntas do Stack Overflow em que pessoas do Java SE estão tentando escrever o Hello World para Java ME MIDP. Nota O ponto de entrada MIDP não possui mainnem static.

 

Conclusão

Com base no exposto, eu diria isso e static, nos momentos de criação de Java e C #, as escolhas mais razoáveis ​​para definir o ponto de entrada do programa .mainString[] args

 

Apêndice: Preciso de suas roupas, suas botas e sua motocicleta

Tenho que admitir, ler o VM Spec 2.17.1 foi muito divertido.

... a linha de comando

java Terminator Hasta la vista Baby!

iniciará uma máquina virtual Java chamando o método main da classe Terminator(uma classe em um pacote sem nome) e transmitindo a ele uma matriz contendo as quatro strings "Hasta", "la", "vista" e "Baby!".

Agora, descrevemos as etapas que a máquina virtual pode executar para executar Terminator, como um exemplo dos processos de carregamento, vinculação e inicialização descritos mais adiante nas seções posteriores.

A tentativa inicial ... descobre que a classe Terminatornão está carregada ...

Depois de Terminatorcarregado, ele deve ser inicializado antes que o main possa ser chamado e um tipo (classe ou interface) sempre deve ser vinculado antes de ser inicializado. A vinculação (§2.17.3) envolve verificação, preparação e (opcionalmente) resolução ...

A verificação (§2.17.3) verifica se a representação carregada de Terminatorestá bem formada ...

A resolução (§2.17.3) é o processo de verificação de referências simbólicas da classe Terminator...

 
Referências simbólicas de Terminatoroh sim.


2
Por alguma razão, tive dificuldade em acreditar que "modernidade" era uma palavra real.
someguy

A história da resposta @Songo também é como um filme. Ele foi publicado pela primeira vez na meta , em uma discussão sobre o fechamento da pergunta: "Se a pergunta fosse reaberta, provavelmente eu escreveria uma resposta como abaixo ...".
Gnat

16

Isso me parece vagamente abusivo. Um construtor é usado para a inicialização de um objeto: ele configura um objeto, que é então usado pelo código que o criou.

Se você colocar a funcionalidade básica de uso dentro do construtor e nunca usar realmente o objeto que o construtor cria no código externo, estará violando os princípios do OOP. Basicamente, fazer algo realmente estranho sem motivo aparente.

Por que você faria isso de qualquer maneira?


5
Mas a “instância do aplicativo” não é logicamente um objeto? Por que isso seria abusivo? Quanto ao uso do objeto - ele tem um objetivo: representar o aplicativo em execução. Parece muito SoC para mim. “Por que você gostaria de fazer isso?” - Estou apenas interessado na lógica da decisão, pois acho que ela está em desacordo com o resto da mentalidade.
21430 Konrad Rudolph

7
@KonradRudolph: Geralmente, espera-se que um construtor, como um getter de propriedades, seja concluído em um tempo limitado sem aguardar a ocorrência de algum evento assíncrono (como entrada do usuário). Seria possível ter um construtor que iniciasse um encadeamento de aplicativo principal, mas isso adiciona um nível de complexidade que pode não ser necessário para todos os aplicativos. Exigir que um aplicativo de console que simplesmente imprima "Hello world" na saída padrão deva gerar um encadeamento extra seria ridículo. O uso de um Mainmétodo funciona bem para o caso simples e não é realmente um problema em casos mais difíceis; então, por que não?
22712

9

Para Java, acho que o raciocínio é simples: ao desenvolver Java, os desenvolvedores sabiam que a maioria das pessoas que aprendia a linguagem conheceria C / C ++ de antemão.

Portanto, o Java não apenas se parece muito com o C / C ++ em vez de dizer smalltalk, mas também assumiu as idiossincrasias do C / C ++ (pense apenas em literais inteiros octais). Como o c / c ++ usa um método principal, fazer o mesmo para java fazia sentido desse ponto de vista.

Tenho certeza de que me lembro de bloch ou de alguém dizendo algo nesse sentido sobre por que eles adicionaram literais de número inteiro octal, vou ver se consigo encontrar algumas fontes :)


2
Se a aparência do C ++ era tão importante para Java, por que eles, por exemplo, mudaram :para extends? E public static void main(String [ ] args)dentro de uma classe é bem diferente do que int main(int argc, char **argv)fora de uma classe.
svick

2
@svick Uma possibilidade: o Java introduziu interfaces e claramente queria separar os dois conceitos (herdar interfaces / classes) - com apenas uma "palavra-chave" que não funcionaria. E "bem diferente"? É o mapeamento possível mais próximo e, até agora, nunca vi um programador c ++ ter um problema ao entender que o método principal estático é o ponto de entrada. Ao contrário do que ter uma classe chamada Application ou algo cujo construtor seja usado, é algo que pareceria estranho para a maioria dos programadores de c ++.
Voo

O @svick int em c para anular o java teve a ver com a forma como um código de retorno de um aplicativo foi gerado - em java, seu 0, a menos que System.exit (int) seja chamado. A mudança de parâmetros tem a ver com a maneira como as matrizes de strings são passadas em cada idioma. Tudo em java está em uma classe - não há opção para tê-lo em outro lugar. Mudar :para extendsé uma questão de sintaxe e é essencialmente o mesmo. Todo o resto é ditado pelo idioma.

@ MichaelT Mas todas essas são decisões de design que tornam o Java diferente do C ++. Então, por que manter Java como C ++ seria importante no caso de main(), quando aparentemente não era importante o suficiente em outros casos?
svick

@svick Exceto que não há problema em não retornar nada do main em C também e essas trivialidades dificilmente confundiriam alguém. O objetivo não era recriar c ++ e todos os seus erros, mas apenas tornar o programador mais em casa. O que você acha que um programador C ++ terá mais facilidade em ler: código Java ou objetivo-c? O que você acha que parecerá mais óbvio para um programador de C ++ um método principal ou um construtor de alguma classe como ponto de entrada?
Voo

6

Bem, existem muitas funções principais por aí que executam um loop infinito. Um construtor que trabalha dessa maneira (com um objeto que nunca é construído) é o que me parece estranho.

Há tantas coisas engraçadas sobre esse conceito. Sua lógica rodando em cima de um objeto por nascer, objetos que nasceram para morrer (já que eles fazem todo o seu trabalho no construtor), ...

Todos esses efeitos colaterais não corromperão muito mais o vagão OO do que um simples público (porque precisa ser acessado por um desconhecido) estático (porque nenhuma instância é necessária para começarmos) void main (porque é o ponto de entrada )?

Para que um ponto de entrada de função simples e simples exista em Java, público e estático seriam automaticamente necessários. Embora seja um método estático , tudo se resume ao que podemos obter mais perto de uma função simples para realizar o que se deseja: um simples ponto de entrada.

Se você não adotará um ponto de entrada de função simples e claro como um ponto de entrada. O que vem a seguir que não parece estranho como um empreiteiro que não deve ser construído?


11
Eu diria que o problema não era ter funções de primeira classe. Colocar o main () dentro de um objeto (que não é instanciado antes que o main seja chamado) é um pouco antipadrão. Talvez eles devam ter um objeto "aplicativo" que seja construído e execute seu método main () não estático, então você pode colocar a inicialização no construtor, e seria muito melhor do que ter métodos estáticos, apesar de simples = level main () fn também seria bom. O main estático é um pouco complicado, no geral.
Gbjbaanb

3

Você pode executar rapidamente alguns testes independentes em uma classe, durante o desenvolvimento, colocando um main()na classe que você está tentando testar.


11
Isso para mim é provavelmente a razão mais atraente, como também permite que múltiplos pontos de entrada durante o desenvolvimento para testar diferentes configurações, etc.
cgull

0

Você tem que começar de algum lugar. Um main estático é o ambiente de execução mais simples que você pode ter - nenhuma instância de nada (fora da JVM e dos parâmetros simples de cadeia de caracteres) precisa ser criada - para que possa "surgir" com um mínimo de confusão (e baixa probabilidade de um erro de codificação que impede a inicialização, etc.) e pode fazer coisas simples sem muitas outras configurações.

Basicamente, uma aplicação do KISS.

[E, claro, o principal motivo é: por que não?]


3
Como eu disse, não estou convencido disso. Objetos que se instanciado antes, e código é executado antes. Precisaria de uma citação de um dos desenvolvedores originais para me convencer de que esse era o motivo.
21911 Konrad Rudolph

2
A quantidade de trabalho necessária para instanciar uma classe a partir do código C é praticamente idêntica à chamada de método estático. Você ainda precisa fazer as mesmas verificações (a classe existe? Bem, ela tem um construtor público com a assinatura correta? tudo bem, então vá em frente).
Voo

Nenhum objeto de usuário precisa ser criado. O construtor de objetos não é executado. A API é incrivelmente simples. E é o mais fácil de entender.
Daniel R Hicks

0

Para meu entendimento, a principal razão é simples. A Sun era uma empresa Unix que vendia máquinas Unix e Unix é para o que foi projetada a convenção C "principal (args)" para invocar um binário .

Além disso, o Java foi projetado explicitamente para ser fácil de entender para os programadores de C e C ++, portanto não havia uma boa razão para simplesmente não aceitar a convenção em C.

A abordagem escolhida na qual todas as classes podem ter um método de chamada é bastante flexível, especialmente em combinação com a Main-Classlinha no arquivo MANIFEST.MF em um jar executável.


Obviamente, o arquivo jar não foi inventado até muito mais tarde.
Daniel R Hicks

-1

Não é consistente com a filosofia OOP que um programa seria um objeto do ponto de vista do processo do SO, pois não há como ter mais de um por definição.

Além disso, um construtor não é um ponto de entrada de forma alguma.

Parece-me a escolha mais razoável de ter principal como uma função estática, o que realmente é no final do dia. Dada a arquitetura de VMs como JVM e CLR, qualquer outra opção seria desnecessária.


11
Eu acho que você está errado lá. Ele é possível ter mais do que um processo, portanto, mais do que um objecto. Aliás, isso é totalmente equivalente a instanciar Runnableobjetos para ter vários threads.
21912 Konrad Rudolph

Um processo é um programa em execução, e você só pode iniciar um processo uma vez através de um ponto de entrada. Os encadeamentos têm seus próprios pontos de entrada, mas ainda estão no mesmo processo.
Yam Marcovic

11
Como eu disse abaixo da resposta de Someguy, isso não é relevante. O que é relevante é a consistência lógica . Logicamente, os processos são representados como objetos pelo iniciador (SO, JVM, qualquer que seja) e são inicializados.
21412 Konrad Rudolph

@KonradRudolph True, mas a inicialização de um programa é apenas uma parte da inicialização de um processo e não legitima um construtor de programa.
Yam Marcovic
Ao utilizar nosso site, você reconhece que leu e compreendeu nossa Política de Cookies e nossa Política de Privacidade.
Licensed under cc by-sa 3.0 with attribution required.