it-swarm-vi.com

Tại sao các ngôn ngữ được quản lý bộ nhớ như Java, Javascript và C # vẫn giữ từ khóa `new`?

Từ khóa new trong các ngôn ngữ như Java, Javascript và C # tạo ra một phiên bản mới của một lớp.

Cú pháp này dường như đã được kế thừa từ C++, trong đó new được sử dụng cụ thể để phân bổ một thể hiện mới của một lớp trên heap và trả về một con trỏ cho thể hiện mới. Trong C++, đây không phải là cách duy nhất để xây dựng một đối tượng. Bạn cũng có thể xây dựng một đối tượng trên ngăn xếp mà không cần sử dụng new - và trên thực tế, cách xây dựng các đối tượng này phổ biến hơn nhiều trong C++.

Vì vậy, xuất phát từ nền tảng C++, từ khóa new trong các ngôn ngữ như Java, Javascript và C # có vẻ tự nhiên và rõ ràng đối với tôi. Sau đó, tôi bắt đầu học Python, không có từ khóa new. Trong Python, một cá thể được xây dựng đơn giản bằng cách gọi hàm tạo, như:

f = Foo()

Lúc đầu, điều này có vẻ hơi khó với tôi, cho đến khi tôi nhận ra rằng không có lý do gì để Python có new, vì mọi thứ đều là đối tượng nên không cần phân định giữa các cú pháp xây dựng khác nhau.

Nhưng sau đó tôi nghĩ - điều gì thực sự là điểm của new trong Java? Tại sao chúng ta nên nói Object o = new Object();? Tại sao không chỉ là Object o = Object();? Trong C++ chắc chắn cần có new, vì chúng ta cần phân biệt giữa phân bổ trên heap và phân bổ trên ngăn xếp, nhưng trong Java tất cả các đối tượng được xây dựng trên heap, vậy tại sao thậm chí có từ khóa new? Câu hỏi tương tự có thể được hỏi cho Javascript. Trong C #, điều mà tôi ít quen thuộc hơn, tôi nghĩ new có thể có một số mục đích để phân biệt giữa các loại đối tượng và các loại giá trị, nhưng tôi không chắc chắn.

Bất kể, đối với tôi, dường như nhiều ngôn ngữ xuất hiện sau C++ chỉ đơn giản là "kế thừa" từ khóa new - mà không thực sự cần đến nó. Nó gần giống như một từ khóa . Chúng tôi dường như không cần nó vì bất kỳ lý do gì, nhưng nó vẫn ở đó.

Câu hỏi : Tôi có đúng về điều này không? Hoặc có một số lý do thuyết phục rằng new cần phải có trong C++ - các ngôn ngữ được quản lý bộ nhớ lấy cảm hứng như Java, Javascript và C # chứ không phải Python?

141
Channel72

Quan sát của bạn là chính xác. C++ là một con thú phức tạp và từ khóa new được sử dụng để phân biệt giữa thứ cần thiết delete sau đó và thứ gì đó sẽ được tự động lấy lại. Trong Java và C #, họ đã bỏ từ khóa delete vì trình thu gom rác sẽ chăm sóc nó cho bạn.

Vấn đề là tại sao họ lại giữ từ khóa new? Không nói chuyện với những người viết ngôn ngữ, thật khó để trả lời. Dự đoán tốt nhất của tôi được liệt kê dưới đây:

  • Đó là về mặt ngữ nghĩa đúng. Nếu bạn đã quen thuộc với C++, bạn sẽ biết rằng từ khóa new tạo ra một đối tượng trên heap. Vì vậy, tại sao thay đổi hành vi dự kiến?
  • Nó gọi sự chú ý đến thực tế là bạn đang khởi tạo một đối tượng chứ không phải gọi một phương thức. Với các đề xuất kiểu mã của Microsoft, tên phương thức bắt đầu bằng chữ in hoa để có thể có sự nhầm lẫn.

Ruby nằm ở đâu đó giữa Python và Java/C # khi sử dụng new. Về cơ bản, bạn khởi tạo một đối tượng như thế này:

f = Foo.new()

Nó không phải là một từ khóa, nó là một phương thức tĩnh cho lớp. Điều đó có nghĩa là nếu bạn muốn một singleton, bạn có thể ghi đè cài đặt mặc định của new() để trả về cùng một thể hiện mỗi lần. Nó không nhất thiết được khuyến khích, nhưng nó có thể.

97
Berin Loritsch

Tóm lại, bạn đúng. Từ khóa mới là không cần thiết trong các ngôn ngữ như Java và C #. Dưới đây là một số hiểu biết từ Bruce Eckel, một thành viên của C++ Standard Comitee vào những năm 1990 và sau đó đã xuất bản sách trên Java: http: //www.artima.com/weblogs/viewpost.jsp?thread=260578

cần có một số cách để phân biệt các đối tượng heap với các đối tượng stack. Để giải quyết vấn đề này, từ khóa mới đã được chiếm đoạt từ Smalltalk. Để tạo một đối tượng ngăn xếp, bạn chỉ cần khai báo nó, như trong Cat x; hoặc, với các đối số, Cat x ("găng");. Để tạo một đối tượng heap, bạn sử dụng new, như trong Cat mới; hoặc Cat mới ("găng");. Đưa ra các hạn chế, đây là một giải pháp thanh lịch và nhất quán.

Nhập Java, sau khi quyết định rằng mọi thứ C++ đều được thực hiện kém và quá phức tạp. Điều trớ trêu ở đây là Java có thể và đã đưa ra quyết định vứt bỏ phân bổ ngăn xếp (rõ ràng bỏ qua sự thất bại của các nguyên thủy, mà tôi đã giải quyết ở nơi khác). Và vì tất cả các đối tượng được phân bổ trên Heap, không cần phân biệt giữa stack và heap. Họ có thể dễ dàng nói Cat x = Cat () hoặc Cat x = Cat ("găng"). Hoặc thậm chí tốt hơn, kết hợp kiểu suy luận để loại bỏ sự lặp lại (nhưng điều đó - - và các tính năng khác như đóng cửa - sẽ mất "quá lâu" vì vậy chúng tôi bị mắc kẹt với phiên bản tầm thường của Java thay vào đó; loại suy luận đã được thảo luận nhưng tôi sẽ đặt cược rằng nó sẽ không xảy ra xảy ra. Và không nên, đưa ra các vấn đề trong việc thêm các tính năng mới vào Java).

87
Nemanja Trifunovic

Có hai lý do tôi có thể nghĩ ra:

  • new phân biệt giữa một đối tượng và nguyên thủy
  • Cú pháp new dễ đọc hơn một chút (IMO)

Thứ nhất là tầm thường. Tôi không nghĩ rằng khó khăn hơn để phân biệt hai bên. Thứ hai là một ví dụ về quan điểm của OP, đó là new là loại dự phòng.

Có thể có xung đột không gian tên, mặc dù; xem xét:

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

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

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

Bạn có thể, mà không cần kéo dài quá nhiều trí tưởng tượng, dễ dàng kết thúc bằng một cuộc gọi đệ quy vô hạn. Trình biên dịch sẽ phải thực thi một lỗi "Tên hàm giống như đối tượng". Điều này thực sự sẽ không gây hại, nhưng nó đơn giản hơn nhiều khi sử dụng new, trong đó nêu rõ, Go to the object of the same name as this method and use the method that matches this signature. Java là một ngôn ngữ rất đơn giản để phân tích cú pháp và tôi nghi ngờ điều này hỗ trợ trong lĩnh vực đó.

15
Michael K

Java, đối với một, vẫn có sự phân đôi của C++: không khá mọi thứ đều là một đối tượng. Java có các loại tích hợp (ví dụ: charint) không phải được phân bổ động.

Điều đó không có nghĩa là new thực sự cần thiết trong Java - tập hợp các loại không cần phân bổ động được cố định và biết. Khi bạn xác định một đối tượng, trình biên dịch có thể biết (và trên thực tế, biết) liệu đó có phải là một giá trị đơn giản mà char hoặc một đối tượng phải được phân bổ động.

Tôi sẽ không kiềm chế (một lần nữa) về việc điều này có nghĩa gì về chất lượng thiết kế của Java (hoặc thiếu nó, như trường hợp có thể).

10
Jerry Coffin

Trong JavaScript, bạn cần nó bởi vì hàm tạo trông giống như một hàm bình thường, vậy làm thế nào để JS biết rằng bạn muốn tạo một đối tượng mới nếu nó không dành cho từ khóa mới?

Gọi một hàm JavaScript với toán tử new dẫn đến hành vi khác với việc gọi một hàm không có toán tử new.

Ví dụ:

Date()       //Returns a string

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

foo()        //Returns `undefined`

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

Tôi thích rất nhiều câu trả lời và chỉ muốn thêm điều này:
[.__.] Nó giúp việc đọc mã dễ dàng hơn
[.___.] Không phải tình huống này sẽ xảy ra thường xuyên, nhưng hãy xem xét bạn muốn có một phương thức trong tên của một động từ có cùng tên với một danh từ. C # và ngôn ngữ khác tự nhiên có Word object dành riêng. Nhưng nếu không thì Foo = object() sẽ là kết quả của lệnh gọi phương thức đối tượng hoặc nó sẽ khởi tạo một đối tượng mới. Hy vọng rằng ngôn ngữ không có từ khóa new có sự bảo vệ chống lại tình huống này, nhưng bằng yêu cầu của từ khóa new trước khi gọi hàm tạo, bạn cho phép tồn tại một phương thức có cùng tên như một đối tượng.

4
Kavet Kerek

Thật thú vị, VB does cần nó - sự khác biệt giữa

Dim f As Foo

khai báo một biến mà không gán nó, tương đương với Foo f; trong C # và

Dim f As New Foo()

khai báo biến và gán một thể hiện mới của lớp, tương đương với var f = new Foo(); trong C #, là một sự khác biệt đáng kể. Trong VB trước-.NET, bạn thậm chí có thể sử dụng Dim f As Foo Hoặc Dim f As New Foo - vì không có quá tải cho các nhà xây dựng.

4
Richard Gadsden

Có rất nhiều công cụ lịch sử trong nhiều ngôn ngữ lập trình. Đôi khi nó ở đó theo thiết kế (C++ được thiết kế để tương thích nhất có thể với C), đôi khi, nó chỉ ở đó. Đối với một ví dụ nghiêm trọng hơn, hãy xem xét câu lệnh C switch bị lan truyền bao xa.

Tôi không biết Javascript và C # đủ tốt để nói, nhưng tôi không thấy lý do gì cho new in Java ngoại trừ việc C++ có nó.

3
David Thornley

Trong C #, nó cho phép các kiểu hiển thị trong ngữ cảnh nơi chúng có thể bị che khuất bởi một thành viên. Cho phép bạn có một thuộc tính có cùng tên với loại của nó. Hãy xem xét những điều sau đây,

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

Lưu ý việc sử dụng new cho phép rõ ràng rằng Address là loại Address không phải là thành viên Address. Điều này cho phép một thành viên có cùng tên với loại của nó. Nếu không có điều này, bạn sẽ cần phải thêm tiền tố vào tên của loại để tránh xung đột tên, chẳng hạn như CAddress. Điều này rất có chủ ý vì Anders không bao giờ thích ký hiệu hay bất cứ thứ gì tương tự và muốn C # có thể sử dụng được nếu không có nó. Thực tế nó cũng quen thuộc là một phần thưởng gấp đôi.

3
chuckj

Tôi không chắc liệu Java người thiết kế có suy nghĩ này không nhưng khi bạn nghĩ về các đối tượng là bản ghi đệ quy, thì bạn có thể tưởng tượng rằng Foo(123) không trả về một đối tượng mà là một hàm có điểm cố định là đối tượng được tạo (nghĩa là hàm sẽ trả về đối tượng nếu nó được đưa ra làm đối số được xây dựng). Và mục đích của new là để "buộc nút thắt "và trả về điểm cố định đó. Nói cách khác, new sẽ làm cho" đối tượng tương lai "nhận ra self của nó.

Cách tiếp cận này có thể giúp chính thức hóa việc thừa kế chẳng hạn: bạn có thể mở rộng một hàm đối tượng với một hàm đối tượng khác và cuối cùng cung cấp cho chúng chung self bằng cách sử dụng new:

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

Đây & kết hợp hai hàm đối tượng.

Trong trường hợp này new có thể được định nghĩa là:

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

Thật thú vị, với sự ra đời của chuyển tiếp hoàn hảo, không phải là vị trí new hoàn toàn không cần thiết trong C++. Vì vậy, có thể nói, nó không còn cần thiết trong bất kỳ ngôn ngữ nào được đề cập.

0
DeadMG