Почему языки с поддержкой памяти, такие как Java, Javascript и C #, сохраняют ключевое слово `new`?

Ключевое слово new на таких языках, как Java, Javascript и C #, создает новый экземпляр класса.

Этот синтаксис, кажется, унаследован от C ++, где new используется специально для выделения нового экземпляра класса в куче и возвращает указатель на новый экземпляр. В C ++ это не единственный способ создать объект. Вы также можете построить объект в стеке, не используя new - и на самом деле этот способ построения объектов гораздо более распространен в C ++.

Итак, исходя из фона C ++, ключевое слово new на таких языках, как Java, Javascript и C #, казалось мне естественным и очевидным. Затем я начал изучать Python, у которого нет ключевого слова new. В Python экземпляр создается просто путем вызова конструктора, например:

f = Foo()

Сначала мне это показалось немного, пока не пришло в голову, что у Python нет причин иметь new, потому что все это объект, поэтому нет необходимости устранять неоднозначность между различными конструкторами синтаксисы.

Но потом я подумал - что на самом деле является точкой new в Java? Почему мы должны говорить Object o = new Object();? Почему не просто Object o = Object();? В C ++ существует определенная потребность в new, так как нам нужно различать выделение в куче и выделение в стеке, но в Java все объекты построены в куче, поэтому почему бы и не использовать new ключевое слово? Тот же вопрос можно задать для Javascript. В C #, с которым я гораздо менее знаком, я думаю, что new может иметь определенную цель с точки зрения различения типов объектов и типов значений, но я не уверен.

Несмотря на это, мне кажется, что многие языки, которые появились после того, как C ++ просто «унаследовали» ключевое слово new, не нуждаясь в этом. Это почти как ключевое слово рудиментарное . Кажется, нам это не нужно по какой-либо причине, и все же оно есть.

Вопрос: Правильно ли я об этом? Или есть какая-то веская причина, что new должен быть на языках с поддержкой языка C ++, таких как Java, Javascript и C #, но не Python?

134 голоса | спросил 6 revs, 3 users 67%
Channel72
1 Jam1000000amThu, 01 Jan 1970 03:00:00 +030070 1970, 03:00:00

15 ответов


90

Ваши наблюдения верны. C ++ - это сложный зверь, а ключевое слово new было использовано для различения того, что нужно delete позже, и того, что было бы автоматически исправлено. В Java и C # они сбросили ключевое слово delete, потому что сборщик мусора позаботится об этом для вас.

Проблема в том, почему они сохранили ключевое слово new? Не разговаривая с людьми, которые написали язык, отчасти трудно ответить. Мои лучшие предположения перечислены ниже:

  • Правильно было семантически . Если вы были знакомы с C ++, вы знали, что ключевое слово new создает объект в куче. Итак, зачем менять ожидаемое поведение?
  • Он обращает внимание на то, что вы создаете экземпляр объекта, а не вызываете метод. С рекомендациями стиля кода Microsoft имена методов начинаются с заглавных букв, поэтому может возникнуть путаница.

Ruby находится где-то между Python и Java /C # в использовании new. В основном вы создаете экземпляр объекта следующим образом:

f = Foo.new()

Это не ключевое слово, это статический метод для класса. Это означает, что если вы хотите использовать singleton, вы можете переопределить реализацию по умолчанию new(), чтобы каждый раз возвращать один и тот же экземпляр. Это не обязательно рекомендуется, но это возможно.

ответил Josh S 11 Mayam14 2014, 05:23:55
85

Короче говоря, вы правы. Ключевое слово new является излишним в таких языках, как Java и C #. Вот некоторые идеи от Брюса Экеля, который был членом C ++ Standard Comitee в 1990-х годах, а затем опубликовал книги на Java: http://www.artima.com/weblogs/viewpost.jsp?thread=260578

  

должен быть какой-то способ отличить объекты кучи от объектов стека. Чтобы решить эту проблему, новое ключевое слово было присвоено Smalltalk. Чтобы создать объект стека, вы просто объявляете его, как в Cat x; или, с аргументами, Cat x ("mittens") ;. Чтобы создать объект кучи, вы используете новый, как в новом Cat; или новый кот («варежки») ;. Учитывая ограничения, это изящное и последовательное решение.

     

Введите Java, решив, что все C ++ плохо сделано и слишком сложно. Ирония заключается в том, что Java могла и решила отказаться от распределения стека (явно игнорируя фиаско примитивов, которое я рассмотрел в другом месте). И поскольку все объекты выделены в куче, нет необходимости различать распределение стека и кучи. Они могли бы легко сказать Cat x = Cat () или Cat x = Cat («рукавицы»). Или, что еще лучше, включение вывода типа для исключения повторения (но это - и другие функции, такие как замыкания), взяли бы «слишком долго», поэтому мы застряли с посредственной версией Java вместо этого, вопрос типа был обсужден, но я буду что это не произойдет, и не должно, учитывая проблемы добавления новых функций в Java).

ответил Josh S 11 Mayam14 2014, 05:23:55
14

Есть две причины, по которым я могу думать:

  • new различает объект и примитив
  • Синтаксис new является более читаемым (IMO)

Первое тривиально. Я не думаю, что было бы труднее сказать друг другу в любом случае. Вторая - пример точки OP, которая заключается в том, что new является избыточным.

Однако могут существовать конфликты пространства имен; рассмотреть следующие вопросы:

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

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

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

Вы могли бы, не слишком сильно растягивая воображение, легко получить бесконечно рекурсивный вызов. Компилятор должен будет принудительно выполнить ошибку «Имя функции, такое же, как и объект». Это действительно не повредит, но гораздо проще использовать new, который гласит, Go to the object of the same name as this method and use the method that matches this signature. Java - очень простой язык для синтаксического анализа, и я подозреваю, что это помогает в этой области.

ответил Josh S 11 Mayam14 2014, 05:23:55
10

Java, для одного, все еще имеет дихотомию C ++: не довольно все является объектом. Java имеет встроенные типы (например, char и int), которые не должны выделяться динамически.

Это не значит, что в Java действительно требуется new, но - набор типов, которые не нужно распределять динамически, является фиксированным и известным. Когда вы определяете объект, компилятор может знать (и, фактически, знает), является ли это простое значение char или объект, который должен быть распределен динамически.

Я воздержусь (еще раз) от того, что это означает о качестве дизайна Java (или, если это будет возможно, в этом случае).

ответил Josh S 11 Mayam14 2014, 05:23:55
7

В JavaScript вам это нужно, потому что конструктор выглядит как обычная функция, поэтому как JS должно знать, что вы хотите создать новый объект, если бы это не было для нового ключевого слова?

ответил Josh S 11 Mayam14 2014, 05:23:55
4

Интересно, что VB нужен - различие между

Dim f As Foo

, который объявляет переменную без ее назначения, эквивалентную Foo f; в C # и

Dim f As New Foo()

, который объявляет переменную и назначает новый экземпляр класса, эквивалент var f = new Foo(); в C # является существенным различием. В pre -.NET VB вы даже можете использовать Dim f As Foo или Dim f As New Foo - потому что не было перегрузки конструкторов.

ответил Josh S 11 Mayam14 2014, 05:23:55
4

Мне нравится много ответов и просто хотел бы добавить это:
Это упрощает чтение кода
Не то, чтобы эта ситуация случалась часто, но считайте, что вы хотите использовать метод в имени глагола, который имеет то же имя, что и существительное. C # и другой язык, естественно, имеют слово object reserved. Но если бы это было не так, то Foo = object() был бы результатом вызова метода объекта или он бы создавал новый объект. Надеюсь, что язык без ключевого слова new имеет защиту от этой ситуации, но если у вас есть требование ключевого слова new перед вызовом конструктора, вы разрешаете существование метода с то же имя, что и объект.

ответил Josh S 11 Mayam14 2014, 05:23:55
3

На многих языках программирования много рудиментарного материала. Иногда это по дизайну (C ++ был разработан как можно более совместимый с C), иногда он просто там. Для более вопиющего примера рассмотрим, как распространялся фрагментированный оператор switch.

Я не знаю Javascript и C #, чтобы сказать, но я не вижу причин для switch в Java, за исключением того, что C ++ имел его.

ответил Josh S 11 Mayam14 2014, 05:23:55
3

В C # он допускает типы, видимые в контекстах, где в противном случае они могут быть затенены членом. Это позволяет вам иметь свойство с тем же именем, что и его тип. Рассмотрим следующее:

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

Обратите внимание, что использование new позволяет ясно, что Address - это Address, а не Address член. Это позволяет члену иметь то же имя, что и его тип. Без этого вам нужно будет префикс имени типа, чтобы избежать столкновения имен, например CAddress. Это было очень преднамеренно, поскольку Андерсу никогда не нравилось венгерское обозначение или что-то подобное, и он хотел, чтобы C # был доступен без него. Тот факт, что он был также знаком, был двойным бонусом.

ответил Josh S 11 Mayam14 2014, 05:23:55
0

Интересно, что с появлением совершенной переадресации не требуется размещение new вообще не требуется на C ++. Таким образом, возможно, он больше не нужен ни на одном из упомянутых языков.

ответил Josh S 11 Mayam14 2014, 05:23:55
0

Я не уверен, имели ли это в виду разработчики Java, но когда вы думаете об объектах как рекурсивные записи , то вы можете себе представить, что Foo(123) doesn ' t возвращает объект, но функция, чья контрольная точка является создаваемым объектом (т.е. функция, которая возвращает объект, если в качестве аргумента объект создается). И цель new - «связать узел» и вернуть эту контрольную точку. Другими словами, new сделает «объект-быть» осведомленным о его self.

Такой подход может помочь, например, формализовать наследование: вы можете расширить объектную функцию с помощью другой объектной функции и, наконец, предоставить им общий self с помощью new

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

Здесь & объединяет две объектные функции.

В этом случае new может быть определен как:

def new(objectFunction) {
    actualObject = objectFunction(actualObject)
    return actualObject
}
ответил Josh S 11 Mayam14 2014, 05:23:55
-2

Чтобы разрешить «ноль».

Наряду с распределением на основе кучи, Java охватывала использование объектов nil. Не только как артефакт, но и как особенность. Когда объекты могут иметь состояние nil, вам нужно отделить объявление от инициализации. Следовательно, новое ключевое слово.

В C ++ строка типа

Person me;

Немедленно вызовет конструктор по умолчанию.

ответил Josh S 11 Mayam14 2014, 05:23:55
-2

Причина, по которой Python не нуждается в этом, связана с его системой типов. В основном, когда вы видите foo = bar() в Python, не имеет значения, является ли метод bar(), который вызывается, класс, который экземпляр или объект-функтор; в Python нет фундаментального различия между функтором и функцией (потому что методы являются объектами); и вы даже можете утверждать, что создаваемый класс является частным случаем функтора. Таким образом, нет причин создавать искусственную разницу в том, что происходит (тем более, что управление памятью настолько чрезвычайно скрыто в Python).

На языке с другой системой набора текста или в тех случаях, когда методы и классы не являются объектами, существует возможность более сильной концептуальной разницы между созданием объекта и вызовом функции или функтора. Таким образом, даже если компилятору /интерпретатору не требуется ключевое слово new, понятно, что он может поддерживаться на языках, которые не нарушили все границы, которые имеет Python.

ответил Josh S 11 Mayam14 2014, 05:23:55
-4

На самом деле в Java существует разница при использовании строк:

String myString = "myString";

отличается от

String myString = new String("myString");

Предположим, что строка "myString" никогда ранее не использовалась, первый оператор создает один String-объект в пуле String (специальная область кучи), в то время как второй оператор создает два объекта, один в нормальной области кучи для объектов и других в пуле строк. Совершенно очевидно, что второй оператор бесполезен в этом конкретном сценарии, но допускается языком.

Это означает, что новый гарантирует, что объект создается в этой специальной области кучи, выделенной для создания обычного объекта, но могут быть другие способы создания объектов без него; приведенный выше пример, и есть место для расширения.

ответил Josh S 11 Mayam14 2014, 05:23:55
-8

В C # new важно различать объекты, созданные в стеке, против кучи. Часто мы создаем объекты в стеке для лучшей производительности. См., Когда объект в стеке выходит из области видимости, он исчезает сразу же, когда стек распадается, как примитив. Нет необходимости, чтобы сборщик мусора отслеживал его, и нет лишних накладных расходов диспетчера памяти, чтобы выделить необходимое пространство в куче.

ответил Josh S 11 Mayam14 2014, 05:23:55

Похожие вопросы

Популярные теги

security × 330linux × 316macos × 2827 × 268performance × 244command-line × 241sql-server × 235joomla-3.x × 222java × 189c++ × 186windows × 180cisco × 168bash × 158c# × 142gmail × 139arduino-uno × 139javascript × 134ssh × 133seo × 132mysql × 132