Por que as linguagens gerenciadas por memória, como Java, Javascript e C #, mantêm a palavra-chave 'new'?

136

A palavra-chave new em linguagens como Java, Javascript e C # cria uma nova instância de uma classe.

Essa sintaxe parece ter sido herdada de C ++, onde new é usado especificamente para alocar uma nova instância de uma classe no heap e retornar um ponteiro para a nova instância. Em C ++, essa não é a única maneira de construir um objeto. Você também pode construir um objeto na pilha, sem usar new - e, de fato, essa maneira de construir objetos é muito mais comum em C ++.

Assim, vindo de um plano de fundo em C ++, a palavra-chave new em linguagens como Java, Javascript e C # parecia natural e óbvia para mim. Então comecei a aprender Python, que não tem a palavra-chave new . Em Python, uma instância é construída simplesmente chamando o construtor, como:

f = Foo()

Primeiramente, isso pareceu um pouco difícil para mim, até me ocorreu que não há razão para o Python ter new , porque tudo é um objeto, então não há necessidade de desambiguar entre as várias sintaxes de construtor.

Mas então eu pensei - qual é realmente o ponto de new em Java? Por que devemos dizer Object o = new Object(); ? Por que não apenas Object o = Object(); ? Em C ++, definitivamente há uma necessidade de new , já que precisamos distinguir entre alocar no heap e alocar na pilha, mas em Java todos os objetos são construídos no heap, então por que ter a palavra-chave new ? A mesma pergunta poderia ser pedida para o Javascript. Em C #, com o qual estou muito menos familiarizado, acho que new pode ter algum propósito em termos de distinguir entre tipos de objeto e tipos de valor, mas não tenho certeza.

Independentemente disso, parece-me que muitas linguagens que vieram depois do C ++ simplesmente "herdaram" a palavra-chave new - sem realmente precisar dela. É quase como uma palavra-chave vestigial . Parece que não precisamos disso por nenhum motivo e, no entanto, está aí.

Pergunta: Estou correto sobre isso? Ou existe alguma razão convincente de que new precisa estar em linguagens gerenciadas por memória inspirada em C ++, como Java, Javascript e C #, mas não em Python?

    
por Channel72 14.04.2014 / 02:20
fonte

15 respostas

92

Suas observações estão corretas. C ++ é uma fera complicada, e a palavra-chave new foi usada para distinguir entre algo que precisava de delete depois e algo que seria automaticamente recuperado. Em Java e C #, eles descartaram a palavra-chave delete porque o coletor de lixo cuidaria disso para você.

O problema é por que eles mantêm a palavra-chave new ? Sem falar com as pessoas que escreveram a língua, é difícil responder. Meus melhores palpites estão listados abaixo:

  • Foi semanticamente correta. Se você estava familiarizado com o C ++, sabia que a palavra-chave new cria um objeto no heap. Então, por que mudar o comportamento esperado?
  • Chama a atenção para o fato de você estar instanciando um objeto em vez de chamar um método. Com as recomendações de estilo de código da Microsoft, os nomes dos métodos começam com letras maiúsculas para que possa haver confusão.

O Ruby está em algum lugar entre Python e Java / C # no uso de new . Basicamente, você instancia um objeto como este:

f = Foo.new()

Não é uma palavra-chave, é um método estático para a classe. O que isso significa é que, se você quiser um singleton, poderá substituir a implementação padrão de new() para retornar sempre a mesma instância. Não é necessariamente recomendado, mas é possível.

    
por 14.02.2011 / 16:58
fonte
86

Em suma, você está certo. A palavra-chave new é supérflua em linguagens como Java e C #. Aqui estão alguns insights de Bruce Eckel, que foi membro do C ++ Standard Comitee em 1990 e mais tarde publicou livros sobre Java: link

there needed to be some way to distinguish heap objects from stack objects. To solve this problem, the new keyword was appropriated from Smalltalk. To create a stack object, you simply declare it, as in Cat x; or, with arguments, Cat x("mittens");. To create a heap object, you use new, as in new Cat; or new Cat("mittens");. Given the constraints, this is an elegant and consistent solution.

Enter Java, after deciding that everything C++ is badly done and overly complex. The irony here is that Java could and did make the decision to throw away stack allocation (pointedly ignoring the debacle of primitives, which I've addressed elsewhere). And since all objects are allocated on the heap, there's no need to distinguish between stack and heap allocation. They could easily have said Cat x = Cat() or Cat x = Cat("mittens"). Or even better, incorporated type inference to eliminate the repetition (but that -- and other features like closures -- would have taken "too long" so we are stuck with the mediocre version of Java instead; type inference has been discussed but I will lay odds it won't happen. And shouldn't, given the problems in adding new features to Java).

    
por 14.02.2011 / 18:00
fonte
15

Há dois motivos pelos quais posso pensar:

  • new distingue entre um objeto e um primitivo
  • A sintaxe new é um pouco mais legível (IMO)

O primeiro é trivial. Eu não acho que seja mais difícil separar os dois de qualquer maneira. O segundo é um exemplo do ponto do OP, que é que new é uma espécie de redundante.

Pode haver conflitos de namespace, no entanto; considere:

public class Foo {
   Foo() {
      // Constructor
   }
}

public class Bar {
   public static void main(String[] args) {
      Foo f = Foo();
   }

   public Foo Foo() {
      return Foo();
   }
}

Você poderia, sem muito esforço da imaginação, facilmente acabar com uma chamada infinitamente recursiva. O compilador teria que impor um erro "Nome da função mesmo como objeto". Isso realmente não faria mal, mas é muito mais simples usar new , que afirma que Go to the object of the same name as this method and use the method that matches this signature. Java é uma linguagem muito simples de analisar, e eu suspeito que isso ajude nessa área.

    
por 30.10.2012 / 13:15
fonte
10

Java, por um lado, ainda tem a dicotomia de C ++: não bastante tudo é um objeto. Java tem tipos internos (por exemplo, char e int ) que não precisam ser alocados dinamicamente.

Isso não significa que new é realmente necessário em Java - o conjunto de tipos que não precisam ser alocados dinamicamente é fixo e conhecido. Quando você define um objeto, o compilador poderia saber (e, de fato, sabe) se é um valor simples que char ou um objeto que tenha que ser alocado dinamicamente.

Vou me abster (mais uma vez) de opinar sobre o que isso significa sobre a qualidade do design do Java (ou a falta dele, conforme o caso).

    
por 14.02.2011 / 17:17
fonte
8

Em JavaScript você precisa dele porque o construtor se parece com uma função normal, então como JS deve saber que você quer criar um novo objeto se não fosse pela nova palavra-chave?

    
por 14.02.2011 / 21:57
fonte
4

Curiosamente, o VB faz precisar dele - a distinção entre

Dim f As Foo

que declara uma variável sem atribuí-la, o equivalente a Foo f; em C # e

Dim f As New Foo()

, que declara a variável e atribui uma nova instância da classe, o equivalente a var f = new Foo(); em C #, é uma distinção substantiva. No pre-.NET VB, você poderia até mesmo usar Dim f As Foo ou Dim f As New Foo - porque não havia sobrecarga em construtores.

    
por 14.02.2011 / 21:38
fonte
4

Gosto de muitas respostas e gostaria apenas de acrescentar isto:
Torna a leitura do código mais fácil
Não que essa situação acontecesse com frequência, mas considere que você queria um método no nome de um verbo que compartilhava o mesmo nome de um substantivo. C # e outros idiomas naturalmente têm a palavra object reservada. Mas se não fosse, seria Foo = object() o resultado da chamada de método de objeto ou seria instanciar um novo objeto. Espero que a linguagem sem a palavra-chave new tenha proteções contra essa situação, mas tenha o requisito da palavra-chave new antes da chamada de um construtor para permitir a existência de um método com o mesmo nome de um objeto. >     

por 15.02.2011 / 14:32
fonte
3

Há muitas coisas vestigiais em muitas linguagens de programação. Às vezes, está lá por design (o C ++ foi projetado para ser o mais compatível possível com C), às vezes, está apenas lá. Para um exemplo mais notório, considere o quanto a instrução C switch quebrada foi propagada.

Eu não sei bem o JavaScript e o C #, mas não vejo razão para new em Java, exceto que o C ++ o tinha.

    
por 14.02.2011 / 16:45
fonte
3

Em C #, ele permite tipos visíveis em contextos em que, de outra forma, poderiam ser obscurecidos por um membro. O permite que você tenha uma propriedade com o mesmo nome de um tipo. Considere o seguinte,

class Address { 
    public string Street { get; set; }
    public string City { get; set; }
    public string State { get; set; }
    public string Zip { get; set; }
}

class Person {
    public string Name { get; set; }
    public Address Address { get; set; }

    public void ChangeStreet(string newStreet) {
        Address = new Address { 
            Street = newStreet, 
            City = Address.City,
            State = Address.State,
            Zip = Address.Zip
        };
    }
}

Observe que o uso de new permite deixar claro que Address é o tipo Address e não o membro Address . Isso permite que um membro tenha o mesmo nome do seu tipo. Sem isso, você precisaria prefixar o nome do tipo para evitar a colisão de nomes, como CAddress . Isso foi muito intencional, pois Anders nunca gostou de notação húngara ou qualquer coisa parecida e queria que o C # fosse utilizável sem ele. O fato de que também era familiar era um bônus duplo.

    
por 12.05.2012 / 05:37
fonte
0

Curiosamente, com o advento do encaminhamento perfeito, o new não posicionado não é obrigatório em todos os C ++. Então, sem dúvida, não é mais necessário em nenhum dos idiomas mencionados.

    
por 27.10.2011 / 21:35
fonte
0

Não tenho certeza se os designers de Java tinham isso em mente, mas quando você pensa em objetos como registros recursivos , pode imaginar que Foo(123) não retorna um objeto, mas uma função cuja fixpoint é o objeto que está sendo criado (isto é, a função que retornaria o objeto se for dado como argumento o objeto sendo construído). E a finalidade de new é "amarrar o nó" e retornar esse ponto de correção. Em outras palavras, new tornaria o "objeto-a-ser" ciente de seu self .

Esse tipo de abordagem poderia ajudar a formalizar a herança, por exemplo: você poderia estender uma função de objeto com outra função de objeto e, finalmente, fornecê-la com self usando new :

cp = new (Colored("Blue") & Line(0, 0, 100, 100))

Aqui & combina duas funções de objeto.

Nesse caso, new pode ser definido como:

def new(objectFunction) {
    actualObject = objectFunction(actualObject)
    return actualObject
}
    
por 31.10.2011 / 16:15
fonte
-2

Para permitir 'nulo'.

Juntamente com a alocação baseada em heap, o Java adotou o uso de objetos nulos. Não apenas como um artefato, mas como um recurso. Quando os objetos podem ter um estado nulo, você deve separar a declaração da inicialização. Daí a nova palavra-chave.

Em C ++, uma linha como

Person me;

Chamaria instantaneamente o construtor padrão.

    
por 29.10.2012 / 07:41
fonte
-2

A razão pela qual o Python não precisa disso é por causa de seu sistema de tipos. Fundamentalmente, quando você vê foo = bar() em Python, não importa se bar() é um método que está sendo invocado, uma classe que está sendo instanciada ou um objeto functor; no Python, não há diferença fundamental entre um functor e uma função (porque os métodos são objetos); e você poderia até argumentar que uma classe sendo instanciada é um caso especial de um functor. Assim, não há razão para criar uma diferença artificial no que está acontecendo (especialmente porque o gerenciamento de memória é tão extremamente oculto no Python).

Em uma linguagem com um sistema de digitação diferente, ou em quais métodos e classes não são objetos, existe a possibilidade de uma diferença conceitual mais strong entre criar um objeto e chamar uma função ou um functor. Assim, mesmo que o compilador / interpretador não precise da palavra-chave new , é compreensível que ela possa ser mantida em linguagens que não quebraram todos os limites que o Python possui.

    
por 29.01.2013 / 22:32
fonte
-4

Na verdade, em Java, há uma diferença ao usar Strings:

String myString = "myString";

é diferente de

String myString = new String("myString");

Supondo que a string "myString" nunca tenha sido usada antes, a primeira instrução cria um único objeto String no pool String (uma área especial do heap) enquanto a segunda instrução cria dois objetos, um na área de heap normal para objetos e outros no conjunto de cadeias. É bastante óbvio que a segunda afirmação é inútil neste cenário específico, mas é permitida pela linguagem.

Isso significa que new assegura que o objeto é criado naquela área especial do heap, alocada para instanciação de objeto normal, mas pode haver outras formas de instanciar objetos sem ele; o acima é um exemplo, e há espaço para extensibilidade.

    
por 31.10.2012 / 10:06
fonte
-8

Em C # novo é importante distinguir entre objetos criados na pilha versus o heap. Freqüentemente criamos objetos na pilha para melhor desempenho. Veja, quando um objeto na pilha sai do escopo, ele desaparece imediatamente quando a pilha se desfaz, assim como um primitivo. Não há necessidade de o coletor de lixo rastreá-lo e não há sobrecarga extra do gerenciador de memória para alocar o espaço necessário no heap.

    
por 29.10.2012 / 03:50
fonte