Naveguei na web em busca do uso de "novo" em Java e é para criar uma nova instância de classe. Por que isso exige indicação explícita por meio da palavra-chave “nova”? Não está entendido que uma invocação de construtor instancia um novo objeto? Sem new
, por exemplo, fica claro que MyClass AnInstance = MyClass(AnArgument)
cria um novo objeto.
De uma perspectiva prática, meu motivo para perguntar é porque presumo que “novo” foi projetado para proteger contra um perigo e quero estar ciente do perigo.
Correndo o risco de uma resposta ser considerada "uma opinião", eu jogaria fora o que
java
seguiu C e C++ e essencialmente herdei a sintaxe deste último - mesmo que o análogodestroy
fosse desnecessário, dado o coletor de lixo.Portanto, não havia um "requisito" estrito para o
new
, mas sim seguir o padrão da linguagem da qual ele derivava mais fortemente.É mesmo?
Considere esta classe (que é Java genuinamente válida):
Nesse caso,
MyClass(AnArgument)
chama o método nomeadoMyClass
nesta classe e nunca cria uma instância deMyClass
.Acho que seu erro está em pensar que essa
new MyClass(AnArgument)
é a sintaxe para chamar um construtor chamadoMyClass
. Eu diria que não. Em vez disso, é uma sintaxe para criar uma instânciaMyClass
chamando um construtor apropriado. Acontece que a sintaxe para definir construtores envolve a repetição do nome da classe; mas não acho que devemos pensar nisso como o nome do construtor. Observe que nunca escrevemos,new MyClass.MyClass(AnArgument)
mesmo que o construtor não esteja no escopo, e observe que issonew MyGenericClass<T>(AnArgument)
passaT
como o argumento de tipo da classeMyGenericClass
, não como seu construtor (já que os construtores não possuem parâmetros de tipo).É uma questão de namespaces.
Em java, métodos e campos podem ter o mesmo nome e não estão totalmente relacionados. Na verdade, os tipos também podem, e os pacotes também. Todos os 4 são completamente ortogonais. Nunca os 4 se encontrarão :
Isso é totalmente legítimo. Nada substitui nada aqui - essa é uma classe nomeada
foo
em packagefoo
, com 3 membros: um campo chamadofoo
, um construtor que aceita zero argumentos e um método nomeadofoo
que não aceita argumentos e não retorna nada. Você pode compilá-lo. Ele vai. Sem avisos. Seu IDE provavelmente reclamará que não é convencional iniciar um tipo com uma letra minúscula. Mas isso é apenas convenção. A especificação lang permite isso.javac
irá compilar o acima perfeitamente.Então, se você pode ter todas essas coisas com o mesmo nome, como isso funciona?
Do contexto, o java sempre sabe o que você está fazendo.
Em algumas linguagens, os parênteses são opcionais ao invocar métodos, ou, pelo menos, tudo está no mesmo namespace.
Por exemplo, em javascript:
É legal;
x
agora é uma referência àtest
função.Isso também significa que você não pode , de fato, ter um método e uma variável com o mesmo nome em javascript:
Não funciona. Essa última instrução tenta executar '5' como uma função que não é.
Em java, esse não é o caso (esses parênteses não são opcionais e, sem os parênteses, simplesmente não se trata desse método, ponto final), porque java é único por ter 4 namespaces completamente não relacionados.
Daí porque o
new
DEVE existir. Porque se não estivesse lá, java não saberia qual dos dois namespaces relevantes (tipos e métodos) explorar. Dito de forma diferente, isto:é java legal. Compila bem. Então, se importar esta classe Test e depois escrever:
Test();
, de qual eu estava falando?Em java, é claro: se você escrever
new Test()
, é o construtor, se você escreverTest()
, é o método estático.Podemos debater longamente se foi uma ideia sábia ter namespaces ortogonais. No entanto, java não gosta de quebrar a compatibilidade com versões anteriores e, neste ponto , mudar tudo claramente não vale a pena a dor que causaria, então sua pergunta se resumiria a: "Por que os designers de java decidiram usar namespaces ortogonais 30 anos atrás", o que é difícil de responder em uma pergunta SO, eu precisaria de um chicote, um chapéu marrom e um pouco de música de John Williams.
No nível da classe, esses construtores são compilados como métodos especiais, que agem principalmente como métodos estáticos (no sentido de que não fazem pesquisa virtual e não requerem um receptor - propriedades que ele compartilha com métodos estáticos e não com métodos de instância), nomeado
<init>
. NãoTest
ou qualquer que seja o nome da sua classe.Então, é melhor pensar neles (pois é assim que funciona):
void
- ou você está declarando um método!) - mas isso é apenas para que o compilador saiba o que você está fazendo. Considere a repetição do nome do seu tipo como uma maneira de Java evitar a introdução de uma palavra-chave (poderia muito bem ter sidopublic constructor(String arg1, int arg2)
em vez depublic TypeNameGoesHere(String arg1, int arg2)
, mas não é. Novamente, 30 anos, evite algumas cobras, se quisermos nos aprofundar no porquê).NB: Os namespaces realmente se encontram. Está nas cadeias AST selecionadas:
a.b.c.d.e
é ambíguo: é aquele packagea.b
, então typec
, com um tipo interno estático chamadod
, que tem um campo estático chamadoe
? Ou é um pacotea
, com classeb
, classe internac
, campo estáticod
, que possui um tipoz
que possui um campo de instância chamadoe
?Javac dá sua melhor facada no escuro durante a compilação e 'trava-o'. No nível de tempo de execução (arquivos de classe), realmente os namespaces não podem se encontrar. Cada instrução deixa claro a qual dos 4 namespaces um identificador está se referindo.
Na minha opinião, o primeiro motivo é a sintaxe da linguagem java, é necessário utilizar a palavra-chave new para criação do objeto. Em segundo lugar, a palavra-chave tecnicamente nova invoca o construtor de uma classe para criar seu objeto e retornar. Desta forma nos permite usar construtores privados (para classes singleton) onde você não pode usar a palavra-chave new para a criação do objeto.