¿Por qué los lenguajes administrados por la memoria como Java, Javascript y C # conservan la palabra clave 'nuevo'?

135

La palabra clave new en lenguajes como Java, Javascript y C # crea una nueva instancia de una clase.

Esta sintaxis parece haber sido heredada de C ++, donde new se usa específicamente para asignar una nueva instancia de una clase en el montón y devolver un puntero a la nueva instancia. En C ++, esta no es la única forma de construir un objeto. También puede construir un objeto en la pila, sin usar new , y de hecho, esta forma de construir objetos es mucho más común en C ++.

Por lo tanto, provenientes de un fondo de C ++, la palabra clave new en lenguajes como Java, Javascript y C # me pareció natural y obvio. Luego empecé a aprender Python, que no tiene la palabra clave new . En Python, una instancia se construye simplemente llamando al constructor, como:

f = Foo()

Al principio, esto me pareció un poco extraño, hasta que se me ocurrió que no hay razón para que Python tenga new , porque todo es un objeto, por lo que no es necesario desambiguar entre las distintas sintaxis del constructor.

Pero luego pensé: ¿cuál es realmente el punto de new en Java? ¿Por qué deberíamos decir Object o = new Object(); ? ¿Por qué no solo Object o = Object(); ? En C ++ definitivamente hay una necesidad de new , ya que necesitamos distinguir entre asignar en el montón y asignar en la pila, pero en Java todos los objetos se construyen en el montón, ¿por qué incluso tener la palabra clave new ? La misma pregunta se puede hacer para Javascript. En C #, con el que estoy mucho menos familiar, creo que new puede tener algún propósito en términos de distinguir entre tipos de objetos y tipos de valores, pero no estoy seguro.

En cualquier caso, me parece que muchos idiomas que vinieron después de C ++ simplemente "heredaron" la palabra clave new , sin que realmente la necesitaran. Es casi como una palabra clave vestigial . Parece que no lo necesitamos por ninguna razón, y sin embargo, está ahí.

Pregunta: ¿Estoy en lo correcto al respecto? ¿O hay alguna razón convincente para que new tenga que estar en lenguajes de gestión de memoria inspirados en C ++ como Java, Javascript y C # pero no en Python?

    
pregunta Channel72 14.04.2014 - 02:20

15 respuestas

90

Sus observaciones son correctas. C ++ es una bestia complicada, y la palabra clave new se usó para distinguir entre algo que necesitaba delete más tarde y algo que se reclamaría automáticamente. En Java y C #, eliminaron la palabra clave delete porque el recolector de basura se encargaría de ti.

El problema entonces es por qué mantuvieron la palabra clave new ? Sin hablar con las personas que escribieron el idioma es difícil responder. Mis mejores conjeturas se enumeran a continuación:

  • Fue semánticamente correcto. Si estaba familiarizado con C ++, sabía que la palabra clave new crea un objeto en el montón. Entonces, ¿por qué cambiar el comportamiento esperado?
  • Llama la atención sobre el hecho de que estás creando una instancia de un objeto en lugar de llamar a un método. Con las recomendaciones de estilo de código de Microsoft, los nombres de los métodos comienzan con letras mayúsculas para que pueda haber confusión.

Ruby está en algún lugar entre Python y Java / C # en su uso de new . Básicamente, creas un objeto como este:

f = Foo.new()

No es una palabra clave, es un método estático para la clase. Lo que eso significa es que si desea un singleton, puede anular la implementación predeterminada de new() para devolver la misma instancia cada vez. No es necesariamente recomendable, pero es posible.

    
respondido por el Berin Loritsch 14.02.2011 - 16:58
86

En resumen, tienes razón. La palabra clave nueva es superflua en lenguajes como Java y C #. Aquí hay algunas ideas de Bruce Eckel que fue miembro del Comité Estándar de C ++ en la década de 1990 y posteriormente publicó libros sobre Java: enlace

  

era necesario que hubiera alguna forma de distinguir los objetos del montón de los objetos de la pila. Para resolver este problema, la nueva palabra clave fue apropiada de Smalltalk. Para crear un objeto de pila, simplemente lo declara, como en Cat x; o, con argumentos, Cat x ("mittens") ;. Para crear un objeto de pila, utiliza new, como en el nuevo Cat; o nuevo gato ("mitones") ;. Dadas las restricciones, esta es una solución elegante y consistente.

     

Ingrese a Java, después de decidir que todo C ++ está mal hecho y es demasiado complejo. La ironía aquí es que Java pudo y tomó la decisión de deshacerse de la asignación de pila (haciendo caso omiso de la debacle de los primitivos, que he abordado en otros lugares). Y dado que todos los objetos se asignan en el montón, no hay necesidad de distinguir entre la asignación de pila y pila. Fácilmente podrían haber dicho Gato x = Gato () o Gato x = Gato ("mitones"). O incluso mejor, incorporó la inferencia de tipos para eliminar la repetición (pero eso, y otras características como los cierres) habrían tomado "demasiado tiempo", por lo que estamos atascados con la versión mediocre de Java; la inferencia de tipos ha sido discutida pero lo haré las probabilidades no van a suceder. Y no deberían, dados los problemas al agregar nuevas funciones a Java).

    
respondido por el Nemanja Trifunovic 14.02.2011 - 18:00
14

Hay dos razones por las que puedo pensar:

  • new distingue entre un objeto y un primitivo
  • La sintaxis de new es un poco más legible (IMO)

El primero es trivial. No creo que sea más difícil distinguirlos de ninguna manera. El segundo es un ejemplo del punto de OP, que es que new es algo redundante.

Sin embargo, podría haber conflictos en el espacio de nombres; considerar:

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

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

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

Podrías, sin estirar demasiado la imaginación, fácilmente terminar con una llamada infinitamente recursiva. El compilador tendría que imponer un error de "Nombre de función igual a objeto". Esto realmente no dolería, pero es mucho más sencillo usar new , que establece que Go to the object of the same name as this method and use the method that matches this signature. Java es un lenguaje muy simple de analizar, y sospecho que esto ayuda en esa área.

    
respondido por el Michael K 30.10.2012 - 13:15
10

Java, por su parte, todavía tiene la dicotomía de C ++: no bastante todo es un objeto. Java tiene tipos incorporados (por ejemplo, char y int ) que no tienen que asignarse dinámicamente.

Eso no significa que new sea realmente necesario en Java, sin embargo, el conjunto de tipos que no necesitan ser asignados dinámicamente es fijo y conocido. Cuando define un objeto, el compilador podría saber (y, de hecho, sí lo sabe) si es un valor simple que char o un objeto que debe asignarse dinámicamente.

Me abstendré (una vez más) de opinar sobre lo que esto significa acerca de la calidad del diseño de Java (o la falta de él, según sea el caso).

    
respondido por el Jerry Coffin 14.02.2011 - 17:17
7

En JavaScript, lo necesita porque el constructor parece una función normal, así que, ¿cómo debería saber JS que desea crear un nuevo objeto si no fuera por la nueva palabra clave?

    
respondido por el user281377 14.02.2011 - 21:57
4

Curiosamente, VB lo necesita, la distinción entre

Dim f As Foo

que declara una variable sin asignarla, el equivalente de Foo f; en C #, y

Dim f As New Foo()

que declara la variable y asigna una nueva instancia de la clase, el equivalente de var f = new Foo(); en C #, es una distinción sustantiva. En pre- .NET VB, incluso podría usar Dim f As Foo o Dim f As New Foo , porque no había sobrecarga en los constructores.

    
respondido por el Richard Gadsden 14.02.2011 - 21:38
4

Me gustan muchas respuestas y solo me gustaría agregar esto:
Hace que la lectura de códigos sea más fácil
No es que esta situación ocurra a menudo, pero considera que querías un método en el nombre de un verbo que compartiera el mismo nombre que un sustantivo. C # y otros idiomas naturalmente tienen la palabra object reservada. Pero si no fuera así, Foo = object() sería el resultado de la llamada al método del objeto o sería una instanciación de un nuevo objeto. Esperemos que dicho lenguaje sin la palabra clave new tenga protecciones contra esta situación, pero al tener el requisito de la palabra clave new antes de llamar a un constructor, se permite la existencia de un método con el mismo nombre que un objeto.

    
respondido por el Kavet Kerek 15.02.2011 - 14:32
3

Hay muchos elementos vestigiales en muchos lenguajes de programación. Algunas veces está ahí por diseño (C ++ fue diseñado para ser lo más compatible posible con C), a veces, simplemente está ahí. Para un ejemplo más atroz, considere hasta qué punto se propagó la declaración C switch rota.

No sé Javascript y C # lo suficientemente bien como para decirlo, pero no veo ninguna razón para new en Java, excepto que C ++ lo tenía.

    
respondido por el David Thornley 14.02.2011 - 16:45
3

En C #, permite que los tipos sean visibles en contextos donde un miembro podría ocultarlos. El le permite tener una propiedad con el mismo nombre que su tipo. Considera lo siguiente,

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
        };
    }
}

Tenga en cuenta que el uso de new permite que quede claro que Address es el tipo Address , no el miembro Address . Esto permite que un miembro tenga el mismo nombre que su tipo. Sin esto, tendría que prefijar el nombre del tipo para evitar la colisión de nombres, como CAddress . Esto fue muy intencional ya que a Anders nunca le gustó la notación húngara o algo similar y quería que C # fuera utilizable sin ella. El hecho de que también fuera familiar fue un doble bono.

    
respondido por el chuckj 12.05.2012 - 05:37
0

Curiosamente, con el advenimiento del reenvío perfecto, tampoco se requiere new de no colocación en C ++. Por lo tanto, podría decirse que ya no es necesario en ninguno de los idiomas mencionados.

    
respondido por el DeadMG 27.10.2011 - 21:35
0

No estoy seguro de si los diseñadores de Java tenían esto en mente, pero cuando piensas que los objetos son registros recursivos , entonces puedes imaginar que Foo(123) no devuelve un objeto sino una función cuya función fixpoint es el objeto que se está creando (es decir, la función que devolvería el objeto si se presenta como argumento el objeto que se está construyendo). Y el propósito de new es "atar el nudo" y devolver ese punto fijo. En otras palabras, new haría que el "objeto a ser" fuera consciente de su self .

Este tipo de enfoque podría ayudar a formalizar la herencia, por ejemplo: podría extender una función de objeto con otra función de objeto y finalmente proporcionarles el self común usando new :

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

Aquí & combina dos funciones de objeto.

En este caso, new podría definirse como:

def new(objectFunction) {
    actualObject = objectFunction(actualObject)
    return actualObject
}
    
respondido por el Aivar 31.10.2011 - 16:15
-2

Para permitir 'nil'.

Junto con la asignación basada en el montón, Java abrazó el uso de objetos nulos. No solo como un artefacto, sino como una característica. Cuando los objetos pueden tener un estado nulo, entonces tiene que separar la declaración de la inicialización. De ahí la nueva palabra clave.

En C ++ una línea como

Person me;

Llamaría instantáneamente al constructor predeterminado.

    
respondido por el Kris Van Bael 29.10.2012 - 07:41
-2

La razón por la que Python no lo necesita es debido a su tipo de sistema. Fundamentalmente, cuando ves foo = bar() en Python, no importa si bar() es un método que se está invocando, una clase a la que se está creando una instancia o un objeto functor; en Python, no hay una diferencia fundamental entre un functor y una función (porque los métodos son objetos); e incluso se podría argumentar que una clase que se crea una instancia es un caso especial de un funtor. Por lo tanto, no hay razón para crear una diferencia artificial en lo que está sucediendo (especialmente porque la gestión de la memoria está extremadamente oculta en Python).

En un lenguaje con un sistema de escritura diferente, o en el que los métodos y las clases no son objetos, existe la posibilidad de una diferencia conceptual más fuerte entre crear un objeto y llamar a una función o functor. Por lo tanto, incluso si el compilador / intérprete no necesita la palabra clave new , es comprensible que se pueda mantener en idiomas que no hayan roto todos los límites que tiene Python.

    
respondido por el Kazark 29.01.2013 - 22:32
-4

En realidad, en Java hay una diferencia cuando se usan cadenas:

String myString = "myString";

es diferente de

String myString = new String("myString");

Suponiendo que la cadena "myString" nunca se ha usado antes, la primera instrucción crea un único objeto String en el conjunto de cadenas (un área especial del montón), mientras que la segunda declaración crea dos objetos, uno en el área del montón normal para Objetos y otros en pool de cuerdas. Es bastante obvio que la segunda afirmación es inútil en este escenario particular, pero el lenguaje lo permite.

Esto significa que New asegura que el objeto se crea en esa área especial del montón, asignado para la creación de instancias de objetos normales, pero puede haber otras formas de crear una instancia de objetos sin él; el anterior es un ejemplo, y queda espacio para la extensibilidad.

    
respondido por el m3th0dman 31.10.2012 - 10:06
-8

En C # nuevo es importante distinguir entre los objetos creados en la pila y el montón. Con frecuencia creamos objetos en la pila para un mejor rendimiento. Vea, cuando un objeto en la pila queda fuera del alcance, desaparece inmediatamente cuando la pila se desenreda, como un primitivo. No es necesario que el recolector de basura realice un seguimiento del mismo, y no hay una sobrecarga adicional del administrador de memoria para asignar el espacio necesario en el montón.

    
respondido por el pds 29.10.2012 - 03:50

Lea otras preguntas en las etiquetas