Tại sao tạo cá thể là như vậy?


17

Tôi đã học C # trong suốt sáu tháng qua và hiện đang đào sâu vào Java. Câu hỏi của tôi là về việc tạo cá thể (bằng cả hai ngôn ngữ, thực sự) và nó còn hơn thế nữa: Tôi tự hỏi tại sao họ lại làm theo cách đó. Lấy ví dụ này

Person Bob = new Person();

Có một lý do mà đối tượng được chỉ định hai lần? Sẽ có bao giờ something_else Bob = new Person()?

Có vẻ như nếu tôi theo dõi từ quy ước thì nó sẽ giống như:

int XIsAnInt;
Person BobIsAPerson;

Hoặc có lẽ một trong số này:

Person() Bob;
new Person Bob;
new Person() Bob;
Bob = new Person();

Tôi cho rằng tôi tò mò liệu có câu trả lời nào hay hơn "đó chỉ là cách nó được thực hiện".


26
Điều gì xảy ra nếu Person là một kiểu con của LivingThing? Bạn có thể viết LivingThing lt = new Person(). Tìm kiếm sự kế thừa và giao diện.
xlecoustillier 7/2/2015

2
Person Bobkhai báo một biến kiểu "tham chiếu đến Person" được gọi Bob. new Person()tạo ra một Personđối tượng. Tài liệu tham khảo, biến và đối tượng là ba thứ khác nhau!
dùng253751

5
Bạn có khó chịu vì sự dư thừa? Vậy thì tại sao không viết var bob = new Person();?
200_success

4
Person Bob();có thể có trong C ++ và có nghĩa gần giống vớiPerson Bob = Person();
user60561

3
@ user60561 không, nó tuyên bố một hàm không có đối số và trả về Person.
Nikolai

Câu trả lời:


52

Sẽ có bao giờ một cái gì đó_else Bob = new Person ()?

Vâng, vì thừa kế. Nếu:

public class StackExchangeMember : Person {}

Sau đó:

Person bob = new StackExchangeMember();
Person sam = new Person();

Bob cũng là một người, và bởi golly, anh ta không muốn bị đối xử khác biệt so với bất kỳ ai khác.

Hơn nữa, chúng ta có thể ban cho Bob những siêu năng lực:

public interface IModerator { }
public class StackOverFlowModerator : StackExchangeMember, IModerator {}

IModerator bob = new StackOverFlowModerator();

Và vì vậy, bởi golly, anh ta sẽ không chịu đối xử khác biệt so với bất kỳ người điều hành nào khác. Và anh ấy thích lén lút trên diễn đàn để giữ mọi người xếp hàng trong khi ẩn danh:

StackExchangeMember bob = new StackOverFlowModerator();

Sau đó, khi anh ta tìm thấy một số poster thứ 1 nghèo nàn, anh ta ném chiếc áo choàng tàng hình của mình và tung lên.

((StackOverFlowModerator) bob).Smite(sam);

Và sau đó anh ta có thể hành động vô tội và những thứ sau đó:

((Person) bob).ImNotMeanIWasJustInstantiatedThatWay();

20
Điều này sẽ rõ ràng hơn rất nhiều nếu bạn đặt tên đối tượng thấp hơn.
Cuộc đua nhẹ nhàng với Monica

38
Ví dụ tốt về đặc điểm kỹ thuật; ví dụ xấu về thừa kế. Đối với bất kỳ ai khác đọc điều này, xin vui lòng, đừng cố gắng giải quyết vai trò của người dùng bằng cách sử dụng tính kế thừa.
Aaronaught 8/2/2015

8
@Aaronaught là chính xác. Đừng tạo các lớp riêng biệt cho các loại người khác nhau. Sử dụng một bitfield enum.
Cole Johnson

1
@Aaronaught Tất cả đều nói rất rõ những điều không nên làm, nhưng nó không hữu ích lắm nếu không nói những gì mọi người nên làm thay thế.
Pharap 8/2/2015

5
@Pharap: Tôi đã làm chính xác điều đó, trong một số câu hỏi khác . Câu trả lời đơn giản là người dùng (xác thực / nhận dạng) và chính sách bảo mật (ủy quyền / quyền) nên được coi là mối quan tâm riêng biệt và các mô hình chuẩn cho chính sách bảo mật là dựa trên vai trò hoặc dựa trên khiếu nại. Kế thừa hữu ích hơn để mô tả đối tượng thực sự xác thực, ví dụ như triển khai LDAP và triển khai SQL.
Aaronaught

34

Hãy lấy dòng mã đầu tiên của bạn và kiểm tra nó.

Person Bob = new Person();

Đầu tiên Personlà một đặc điểm kỹ thuật loại. Trong C #, chúng ta có thể giải quyết vấn đề này bằng cách nói đơn giản

var Bob = new Person();

và trình biên dịch sẽ suy ra kiểu biến Bob từ lệnh gọi hàm tạo Person().

Nhưng bạn có thể muốn viết một cái gì đó như thế này:

IPerson Bob = new Person();

Trường hợp bạn không hoàn thành toàn bộ hợp đồng API của Person mà chỉ là hợp đồng được chỉ định bởi giao diện IPerson.


2
+1: Tôi sẽ thực hiện IPersonví dụ trong mã của mình để đảm bảo rằng tôi không vô tình sử dụng bất kỳ phương thức riêng tư nào khi tôi đang viết mã nên sao chép / dán vào một IPersontriển khai khác .
Cort Ammon - Tái lập lại

@CortAmmon Tôi nghĩ rằng bạn có một lỗi đánh máy ở đó, rõ ràng bạn có nghĩa là "làm việc với đa hình" chứ không phải là sao chép / dán vào mã đó: D
Benjamin Gruenbaum 8/215

@BenjaminGruenbaum chắc chắn, đối với một số định nghĩa về đa hình ;-)
Cort Ammon - Tái lập lại

@CortAmmon bạn sẽ vô tình gọi một phương thức riêng tư như thế nào? Chắc chắn là bạn có ý nghĩa internal?
Cole Johnson

@ColeJohnson một trong hai cách. Tôi đã viết rằng suy nghĩ về một trường hợp cụ thể mà tôi chạy qua nơi privatecó ý nghĩa: các phương thức nhà máy là một phần của lớp được khởi tạo. Trong tình huống của tôi, truy cập vào các giá trị riêng tư phải là ngoại lệ, không phải là tiêu chuẩn. Tôi làm việc với mã sẽ tồn tại lâu hơn tôi. Nếu tôi làm theo cách này, không chỉ tôi không thể sử dụng bất kỳ phương thức riêng tư nào, mà khi nhà phát triển tiếp theo sao chép / dán này vài chục địa điểm và nhà phát triển sau đó sao chép chúng, nó sẽ giảm tỷ lệ người nào đó nhìn thấy cơ hội sử dụng các phương thức riêng tư như hành vi "bình thường".
Cort Ammon - Tái lập lại

21
  1. Cú pháp này gần như là một di sản từ C ++, nhân tiện, có cả hai:

    Person Bob;

    Person *bob = new Bob();

    Cái đầu tiên để tạo một đối tượng trong phạm vi hiện tại, cái thứ hai để tạo một con trỏ tới một đối tượng động.

  2. Bạn chắc chắn có thể có something_else Bob = new Person()

    IEnumerable<int> nums = new List<int>(){1,2,3,4}

    Bạn đang làm hai việc khác nhau ở đây, nêu rõ loại biến cục bộ numsvà bạn nói rằng bạn muốn tạo một đối tượng mới của loại 'Danh sách' và đặt nó ở đó.

  3. Loại C # đồng ý với bạn, bởi vì hầu hết thời gian loại biến giống hệt với những gì bạn đặt trong đó do đó:

    var nums = new List<int>();
  4. Trong một số ngôn ngữ, bạn cố hết sức để tránh nêu rõ các loại biến như trong F # :

    let list123 = [ 1; 2; 3 ]

5
Có lẽ chính xác hơn để nói rằng ví dụ thứ hai của bạn tạo một con trỏ tới một đối tượng Bob mới. Cách mọi thứ được lưu trữ về mặt kỹ thuật là một chi tiết thực hiện.
Robert Harvey

4
"Người đầu tiên tạo một đối tượng cục bộ trên ngăn xếp, lần thứ hai để tạo một đối tượng trên heap." Ôi trời, không phải thông tin sai lệch này nữa.
Cuộc đua nhẹ nhàng với Monica

@LightnessRacesinOrbit tốt hơn?
AK_ 8/2/2015

1
@AK_ Mặc dù "động" khá nhiều có nghĩa là "heap" (khi heap là mô hình bộ nhớ được sử dụng bởi nền tảng), "tự động" rất khác biệt với "stack". Nếu bạn làm như vậy new std::pair<int, char>(), thì các thành viên firstsecondcủa cặp có thời lượng lưu trữ tự động, nhưng chúng có thể được phân bổ trên heap (với tư cách là thành viên của pairđối tượng thời lượng lưu trữ động ).
Phục hồi Monica

1
@AK_: Vâng, những cái tên đó ngụ ý chính xác ý nghĩa của chúng, trong khi "chồng" này với "đống" vô nghĩa thì không . Đó là toàn bộ vấn đề. Rằng bạn thấy chúng khó hiểu chỉ củng cố nhu cầu chúng tôi dạy chúng, để bạn không dựa vào thuật ngữ không chính xác / không chính xác chỉ vì nó quen thuộc!
Cuộc đua nhẹ nhàng với Monica

3

Có một sự khác biệt rất lớn giữa int xPerson bob. An intlà một intlà một intvà nó phải luôn luôn là một intvà không bao giờ có thể là bất cứ điều gì khác hơn là một int. Ngay cả khi bạn không khởi tạo intkhi bạn khai báo ( int x;), nó vẫn intđược đặt thành giá trị mặc định.

Person bobTuy nhiên, khi bạn tuyên bố , có rất nhiều sự linh hoạt về tên mà bobthực sự có thể đề cập đến tại bất kỳ thời điểm nào. Nó có thể đề cập đến một Personhoặc nó có thể đề cập đến một số lớp khác, ví dụ Programmer, có nguồn gốc từ Person; nó thậm chí có thể là null, không đề cập đến đối tượng nào cả.

Ví dụ:

  Person bob   = null;
  Person carol = new Person();
  Person ted   = new Programmer();
  Person alice = personFactory.functionThatReturnsSomeKindOfPersonOrNull();

Các nhà thiết kế ngôn ngữ chắc chắn có thể đã tạo ra một cú pháp thay thế có thể hoàn thành điều tương tự như Person carol = new Person()trong ít biểu tượng hơn, nhưng họ vẫn phải cho phép Person carol = new Person() (hoặc đưa ra một số quy tắc kỳ lạ khiến một trong bốn ví dụ trên trở thành bất hợp pháp). Họ quan tâm đến việc giữ cho ngôn ngữ "đơn giản" hơn là viết mã cực kỳ súc tích. Điều đó có thể đã ảnh hưởng đến quyết định của họ không cung cấp cú pháp thay thế ngắn hơn, nhưng trong mọi trường hợp, điều đó là không cần thiết và họ đã không cung cấp nó.


1

Hai khai báo có thể khác nhau nhưng thường giống nhau. Một mẫu phổ biến, được đề xuất trong Java trông giống như:

List<String> list = new ArrayList<>();
Map<String, Integer> map = new HashMap<>();

Các biến này listmapđược khai báo bằng cách sử dụng các giao diện ListMaptrong khi mã khởi tạo các triển khai cụ thể. Theo cách này, phần còn lại của mã chỉ phụ thuộc vào các giao diện và thật dễ dàng để chọn một lớp triển khai khác để khởi tạo, như TreeMap, vì phần còn lại của mã không thể phụ thuộc vào bất kỳ phần nào của HashMapAPI bên ngoài Mapgiao diện.

Một ví dụ khác có hai loại khác nhau là trong một phương thức xuất xưởng chọn một lớp con cụ thể để khởi tạo, sau đó trả về nó làm kiểu cơ sở để người gọi không cần biết về các chi tiết triển khai, ví dụ như lựa chọn "chính sách".

Kiểu suy luận có thể sửa lỗi dự phòng mã nguồn. Ví dụ: trong Java

List<String> listOne = Collections.emptyList();

sẽ xây dựng đúng loại Danh sách nhờ loại suy luận và khai báo

static <T> List<T> emptyList(); 

Trong một số ngôn ngữ, nhập suy luận đi xa hơn, ví dụ như trong C ++

auto p = new Person();

BTW, ngôn ngữ Java đặt ra một quy ước mạnh mẽ để sử dụng các tên định danh chữ thường, như bob, không Bob. Điều này tránh được rất nhiều sự mơ hồ, ví dụ như gói. Kính so với Class.variable.
Jerry101

... Và đó là lý do tại sao bạn có clazz.
một CVn

Không, clazzđược sử dụng vì classlà một từ khóa nên không thể sử dụng nó làm định danh.
Jerry101

... sẽ không thành vấn đề nếu đặt tên cho các quy ước không như vậy. Classlà một định danh hoàn toàn hợp lệ.
cHao

... như là cLaSs, cLASScLASs.
el.pescado

1

Theo cách nói của giáo dân:

  • Tách khai báo khỏi khởi tạo giúp tách nhóm người sử dụng các đối tượng khỏi người tạo ra chúng
  • Khi bạn làm điều đó, chế độ đa trị được kích hoạt, miễn là kiểu khởi tạo là một kiểu con của kiểu khai báo, tất cả mã sử dụng biến sẽ hoạt động
  • Trong các ngôn ngữ được gõ mạnh, bạn phải khai báo một biến cho biết loại của nó, bằng cách đơn giản là var = new Process()bạn không khai báo biến trước.

0

Nó cũng là về mức độ kiểm soát những gì đang xảy ra. Nếu khai báo của một đối tượng / biến tự động gọi một hàm tạo, ví dụ, nếu

Person somePerson;

được tự động giống như

Person somePerson = new Person(blah, blah..);

sau đó, bạn sẽ không bao giờ có thể sử dụng (ví dụ) các phương thức nhà máy tĩnh để khởi tạo các đối tượng thay vì các hàm tạo mặc định, nghĩa là, có những lúc bạn không muốn gọi hàm tạo cho một đối tượng mới.

Ví dụ này được giải thích trong Java hiệu quả của Joshua Bloch (Mục 1 đủ trớ trêu thay!)


Về sách, cả sách C # và sách Java của tôi đều từ Joyce Farrell. Đó chỉ là những gì khóa học quy định. Tôi cũng đã bổ sung cả hai video youtube khác nhau trên C # và Java.
Jason Wohlgemuth 7/215

Tôi đã không chỉ trích, chỉ đưa ra một tham chiếu đến nơi này đến từ đâu :)
David Scholefield 7/2/2015
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.