it-swarm.cn

为什么像Java,Javascript和C#这样的内存管理语言保留了“ new”关键字?

像Java,Javascript和C#这样的语言中的new关键字会创建类的新实例。

此语法似乎是从C++继承的,其中new专门用于在堆上分配类的新实例,并返回指向该新实例的指针。在C++中,这不是构造对象的唯一方法。您还可以在堆栈上构造对象,而无需使用new-实际上,这种构造对象的方式在C++中更为常见。

因此,来自C++背景,Java,Javascript和C#等语言中的new关键字对我来说似乎很自然并且显而易见。然后,我开始学习没有new关键字的Python。在Python中,只需调用构造函数即可构造实例,例如:

_f = Foo()
_

起初,这对我来说似乎有点不对劲,直到我想到没有理由Python拥有new,因为所有东西都是对象,所以不需要消除各种构造函数语法之间的歧义。

但是后来我想-Java中new的真正含义是什么?我们为什么要说Object o = new Object();?为什么不只是Object o = Object();?在C++中,肯定需要new,因为我们需要区分是在堆上分配还是在堆上分配,但是在Java所有对象都是在堆上构造的,那么为什么甚至要使用new关键字呢?可能会问同样的Java脚本问题。在我不太熟悉的C#中,我认为new可能在区分在对象类型和值类型之间,但是我不确定。

无论如何,在我看来,C++之后出现的许多语言只是“继承”了new关键字-并不需要它。几乎就像搜寻关键字。我们似乎出于任何原因都不需要它,但是它在那里。

问题:我对此是否正确?还是有某些令人信服的理由要求new必须是受C++启发的内存管理语言(例如Java,Javascript和C#)而不是Python?

141
Channel72

您的观察是正确的。 C++是一门复杂的野兽,并且使用new关键字来区分以后需要delete的内容和将自动回收的内容。在Java和C#中,他们删除了delete关键字,因为垃圾收集器会为您处理它。

然后的问题是为什么他们保留new关键字?如果不与写该语言的人交谈,这很难回答。我的最佳猜测如下:

  • 这是正确地如果您熟悉C++,则知道new关键字会在堆上创建一个对象。那么,为什么要改变预期的行为呢?
  • 它引起您注意,您正在实例化一个对象而不是调用一个方法。根据Microsoft代码样式建议,方法名称以大写字母开头,因此可能会造成混淆。

Ruby在Python和Java/C#之间,使用new。)之间的某个地方。基本上,您实例化了这样的对象:

_f = Foo.new()
_

它不是关键字,而是类的静态方法。这意味着如果您要单例,则可以覆盖new()的默认实现,以每次返回相同的实例。不一定推荐它,但是有可能。

97
Berin Loritsch

简而言之,您是对的。关键字new在Java和C#等语言中是多余的。以下是Bruce Eckel的一些见解,他是1990年代C++ Standard Comitee的成员,后来出版了有关Java的书籍: http: //www.artima.com/weblogs/viewpost.jsp?thread=260578

需要某种方法将堆对象与堆栈对象区分开。为了解决此问题,从Smalltalk中使用了new关键字。要创建一个堆栈对象,只需声明它,就像在Cat x中一样;或者带有参数Cat x(“ mittens”);。要创建堆对象,请像新建Cat一样使用new。或新的Cat(“手套”);。考虑到这些限制,这是一个优雅而一致的解决方案。

在确定所有C++都做得不好并且过于复杂之后,请输入Java。具有讽刺意味的是,Java可以并且确实决定放弃堆栈分配(有目的地忽略了基元的崩溃,这在我之前已经解决过))。而且由于所有对象都分配在堆,不需要区分堆栈分配和堆分配,它们可以很容易地说Cat x = Cat()或Cat x = Cat(“ mittens”)。甚至更好的是,通过合并类型推断来消除重复(但是- -和其他功能(例如闭包)将花费“太长的时间”,因此我们只能使用Java的普通版本;我们已经讨论了类型推断,但是我很可能不会鉴于向Java添加新功能存在问题,因此不应该这样做。

87
Nemanja Trifunovic

我可以想到两个原因:

  • 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是一种非常简单的语言,我怀疑这在该领域有帮助。

15
Michael K

Java仍然具有C++的二分法:不是非常一切都是对象。 Java具有不需要动态分配的内置类型(例如charint))。

但这并不意味着new在Java中确实是必要的-不需要动态分配的类型集是固定的并且是已知的。一个对象,编译器可以知道(实际上,的确知道)是char的简单值还是必须动态分配的对象。

我将(再次)避免对Java设计质量(或视情况而定,缺乏质量)这意味着什么。

10
Jerry Coffin

在JavaScript中,您需要它,因为构造函数看起来像普通函数,因此,如果不是new关键字,那么JS应该如何知道要创建一个新对象?

使用new运算符调用JavaScript函数所产生的行为与不使用new运算符调用函数所产生的行为不同。

例如:

Date()       //Returns a string

new Date()   //Returns a Date object
function foo() {};

foo()        //Returns `undefined`

new foo()    //Returns an empty object
10
user281377

我喜欢很多答案,只想添加以下内容:
这使代码阅读更容易
并不是这种情况经常发生,但请考虑您想要一个以与名词同名的动词名称的方法。 C#和其他语言自然会保留字object。但是,如果不是,则Foo = object()是对象方法调用的结果,还是将实例化一个新对象。希望说没有new关键字的语言可以防止这种情况,但是通过在调用构造函数之前需要new关键字的要求,您可以允许存在与以下名称相同的方法一个东西。

4
Kavet Kerek

有趣的是,VB does需要它-

_Dim f As Foo
_

它声明一个变量而不分配它,等效于C#中的_Foo f;_,并且

_Dim f As New Foo()
_

它声明了变量并分配了该类的新实例,这与C#中的var f = new Foo();等效,是一种实质区别。在.NET VB之前的版本中,您甚至可以使用_Dim f As Foo_或_Dim f As New Foo_-因为构造函数上没有重载。

4
Richard Gadsden

许多编程语言中都有很多残留的东西。有时它是有意设计的(C++被设计为与C尽可能兼容),有时,它就在那里。举一个更加极端的例子,考虑一下损坏的C switch语句传播了多远。

我不太了解Java语言和C#,但是我没有理由在Java)中使用new,除了C++拥有它。

3
David Thornley

在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可以清楚地知道AddressAddress类型而不是Address成员。这允许成员使用与其类型相同的名称。否则,您需要在类型名称前添加前缀以避免名称冲突,例如CAddress。这是非常故意的,因为Anders从不喜欢匈牙利符号或任何类似符号,并希望C#在没有它的情况下可用。它也很熟悉的事实是双重好处。

3
chuckj

我不确定Java设计人员)是否想到了这一点,但是当您将对象视为递归记录时,您就可以想象Foo(123)不会返回对象,而是返回其定点为正在创建的对象的函数(即,如果将要构造的对象作为参数给出的函数,则返回该对象)。new的目的是“将对象绑定结”并返回该固定点。换句话说,new将使“要成为对象”意识到其self

例如,这种方法可以帮助形式化继承:您可以使用另一个对象函数扩展对象函数,并最终通过使用self为它们提供通用的new

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

这里 &结合了两个对象功能。

在这种情况下,new可以定义为:

def new(objectFunction) {
    actualObject = objectFunction(actualObject)
    return actualObject
}
0
Aivar

有趣的是,随着完美转发的出现,在C++中也完全不需要非放置new。因此,可以说,上述任何一种语言都不再需要它。

0
DeadMG