Truy cập trường riêng của một đối tượng khác trong cùng một lớp


91
class Person 
{
   private BankAccount account;

   Person(BankAccount account)
   {
      this.account = account;
   }

   public Person someMethod(Person person)
   {
     //Why accessing private field is possible?

     BankAccount a = person.account;
   }
}

Xin vui lòng quên về thiết kế. Tôi biết rằng OOP chỉ định rằng các đối tượng riêng tư là riêng tư đối với lớp. Câu hỏi của tôi là, tại sao OOP được thiết kế để các trường riêng có quyền truy cập mức lớp chứ không phải quyền truy cập mức đối tượng ?


5
Tôi tin rằng OP coi đối tượng "Person" được chuyển đến "someMethod" là một đối tượng riêng biệt và do đó phương thức không nên có quyền truy cập vào các thành viên riêng tư của nó ... mặc dù nó nằm trong lớp "Person".
Inisheer

1
Một số ngôn ngữ không làm được điều này (Báo chí chẳng hạn). Bạn không có khả năng nhận được câu trả lời tốt là tại sao. Bạn sẽ nhận được câu trả lời làm việc ngược lại với những gì sẽ xảy ra được chỉ định.
Tom Hawtin - tackline

Các someMethodkhông hợp lệ. Nó không trả lại bất cứ điều gì. Nó phải được void.
Nicolas Barbulesco

1
Nếu không đúng như vậy, sẽ rất khó viết hàm tạo bản sao và toán tử gán imo.
rozina

Câu trả lời:


69

Tôi cũng hơi tò mò với câu trả lời.

Câu trả lời hài lòng nhất mà tôi tìm thấy là từ Artemix trong một bài đăng khác ở đây (Tôi đang đổi tên AClass bằng lớp Person): Tại sao lại có các công cụ sửa đổi quyền truy cập cấp lớp thay vì cấp đối tượng?

Công cụ sửa đổi riêng thực thi nguyên tắc đóng gói.

Ý tưởng là 'thế giới bên ngoài' không nên thực hiện các thay đổi đối với các quy trình nội bộ của Người vì việc triển khai Người có thể thay đổi theo thời gian (và bạn sẽ phải thay đổi toàn bộ thế giới bên ngoài để khắc phục sự khác biệt trong việc thực hiện - điều này gần như là không thể).

Khi phiên bản Person truy cập vào nội bộ của cá thể Person khác - bạn có thể chắc chắn rằng cả hai phiên bản luôn biết chi tiết về việc triển khai Person. Nếu logic của các quy trình nội bộ với Người được thay đổi - tất cả những gì bạn phải làm là thay đổi mã Người.

CHỈNH SỬA: Hãy bình chọn câu trả lời của Artemix. Tôi chỉ sao chép và dán nó.


4
Đây có lẽ là lý do. Nhưng đây là một ý tưởng tồi . Điều này khuyến khích thực hành xấu. Một nhà phát triển truy cập vào một trường Person, trong lớp Person, không cần phải biết việc triển khai của cả lớp. Thực hành tốt là sử dụng trình truy cập mà không cần phải biết những hoạt động của trình truy cập.
Nicolas Barbulesco

16
@NicolasBarbulesco Tôi nghĩ lý do được đưa ra trong câu trả lời là âm thanh. Ví dụ, bạn muốn triển khai equals(Object)phương thức trong một lớp Java để kiểm tra tính bình đẳng của một Personđối tượng với một phiên bản khác của Person. Bạn có thể muốn cho phép thế giới bên ngoài kiểm tra xem hai trường hợp có bằng nhau hay không, nhưng bạn có thể không muốn để lộ tất cả các trường riêng tư của lớp cần thiết để kiểm tra sự bình đẳng với thế giới bên ngoài bằng cách sử dụng các phương thức của trình truy cập công khai. Có quyền truy cập cấp lớp vào privatecác trường cho phép triển khai một phương thức như vậy mà không cần thiết phải triển khai các phương thức công khai đó.
Malte Skoruppa

1
@MalteSkoruppa - Đây là một ví dụ điển hình (triển khai phương pháp equals).
Nicolas Barbulesco

@MalteSkoruppa - Tuy nhiên, việc triển khai phương pháp này equalscó thể được thực hiện bằng cách gọi những người truy cập riêng.
Nicolas Barbulesco

@NicolasBarbulesco Tất nhiên là có, nhưng vấn đề là, cho dù bạn sử dụng trình truy cập riêng tư hay truy cập trực tiếp vào các trường riêng tư để triển khai phương pháp, privateđều phải cấp quyền truy cập cấp lớp. Tôi đồng ý rằng việc sử dụng trình truy cập nói chung là một thói quen tốt, mặc dù trong trường hợp này, đó chủ yếu là vấn đề về ngữ cảnh và phong cách cá nhân. Lưu ý rằng cả Joshua Bloch trong Java hiệu quả (mục 8) và Tal Cohen trong bài viết Tiến sĩ Dobbs này đều truy cập trực tiếp vào các trường riêng tư trong danh sách mã của họ khi thảo luận về cách triển khai equals.
Malte Skoruppa

17

Xem Đặc tả ngôn ngữ Java, Phần 6.6.1. Xác định khả năng tiếp cận

Nó nói

Ngược lại, nếu thành viên hoặc hàm tạo được khai báo private, thì quyền truy cập được cho phép nếu và chỉ khi nó xảy ra trong phần thân của lớp cấp cao nhất (§7.6) bao quanh khai báo của thành viên hoặc hàm tạo.

Nhấp vào liên kết ở trên để biết thêm chi tiết. Vì vậy, câu trả lời là: Bởi vì James Gosling và các tác giả khác của Java đã quyết định nó theo cách đó.


17

Câu hỏi hay. Có vẻ như công cụ sửa đổi quyền truy cập mức đối tượng sẽ thực thi nguyên tắc Đóng gói hơn nữa.

Nhưng thực ra thì ngược lại. Hãy lấy một ví dụ. Giả sử bạn muốn sao chép sâu một đối tượng trong một phương thức khởi tạo, nếu bạn không thể truy cập vào các thành viên riêng của đối tượng đó. Sau đó, cách duy nhất có thể là thêm một số người truy cập công khai vào tất cả các thành viên riêng tư. Điều này sẽ làm cho các đối tượng của bạn trần trụi đối với tất cả các phần khác của hệ thống.

Vì vậy, đóng gói không có nghĩa là đóng cửa với tất cả phần còn lại của thế giới. Nó có nghĩa là chọn lọc đối tượng mà bạn muốn công khai.


2
Câu trả lời này cần được bình chọn! Các câu trả lời khác chỉ nêu lại 'quy tắc' nhưng chỉ câu trả lời này thực sự tiết lộ lý do đằng sau quy tắc và đi đúng điểm.
mzoz

4
Nhưng sẽ tốt hơn nếu một đối tượng chịu trách nhiệm cung cấp các bản sao của chính nó? Sau đó, nếu bạn cần một bản sao sâu của một đối tượng, không quan trọng nếu bạn là một đối tượng khác của cùng một lớp hay một đối tượng của một lớp khác: đó là cùng một cơ chế o.deepCopy()hoặc bất cứ điều gì.
dirtside

4

Điều này hoạt động bởi vì bạn đang ở trong class Person- một lớp được phép chọc vào bên trong loại lớp của chính nó. Điều này thực sự hữu ích khi bạn muốn viết một hàm tạo bản sao, ví dụ:

class A
{
   private:
      int x;
      int y;
   public:
      A(int a, int b) x(a), y(b) {}
      A(A a) { x = a.x; y = y.x; }
};

Hoặc nếu chúng ta muốn viết operator+operator-cho lớp số lớn của chúng ta.


Đây là một cái gì đó tương tự như tiêm phụ thuộc. Trong đó bạn cũng có thể chèn một số đối tượng của lớp khác, nơi bạn không thể truy cập biến private.
Nageswaran

Chắc chắn, nếu bạn đang cố gắng xây dựng một lớp A từ một đối tượng của lớp B và B có thành phần private, thì A cần được khai báo là bạn hoặc chỉ có thể xem các phần công khai [có thể được bảo vệ, nếu A có nguồn gốc từ B ].
Mats Petersson

Trong java và .net không có khái niệm bạn bè. Trong những trường hợp như vậy, làm thế nào để xử lý này?
Nageswaran

1

Chỉ 2 xu của tôi cho câu hỏi tại sao ngữ nghĩa của khả năng hiển thị riêng tư trong Java là cấp lớp chứ không phải cấp đối tượng.

Tôi có thể nói rằng sự tiện lợi dường như là chìa khóa ở đây. Trên thực tế, khả năng hiển thị riêng tư ở mức đối tượng sẽ buộc phải hiển thị các phương thức cho các lớp khác (ví dụ trong cùng một gói) trong kịch bản được minh họa bởi OP.

Trong thực tế, tôi không thể hiểu và cũng không thể tìm thấy một ví dụ cho thấy rằng khả năng hiển thị ở cấp độ lớp riêng tư (như được cung cấp bởi Java) tạo ra bất kỳ vấn đề nào nếu so sánh với khả năng hiển thị ở cấp độ đối tượng-riêng.

Điều đó nói rằng, các ngôn ngữ lập trình có hệ thống chính sách khả năng hiển thị chi tiết hơn có thể đủ khả năng hiển thị đối tượng ở cấp đối tượng và cấp lớp.

Ví dụ: Eiffel , cung cấp tính năng xuất có chọn lọc: bạn có thể xuất bất kỳ đối tượng địa lý nào của lớp sang bất kỳ lớp nào bạn chọn, từ {NONE} (đối tượng-riêng tư) đến {ANY} (tương đương với công khai và cũng là mặc định), sang {PERSON} (lớp-riêng tư, xem ví dụ của OP), cho các nhóm lớp cụ thể {PERSON, BANK}.

Cũng rất thú vị khi nhận xét rằng trong Eiffel, bạn không cần đặt một thuộc tính là riêng tư và viết một getter để ngăn các lớp khác gán cho nó. Các thuộc tính công khai trong Eiffel theo mặc định có thể truy cập ở chế độ chỉ đọc, vì vậy bạn không cần getter chỉ để trả lại giá trị của chúng.

Tất nhiên, bạn vẫn cần một setter để đặt một thuộc tính, nhưng bạn có thể ẩn nó bằng cách xác định nó là "người gán" cho thuộc tính đó. Điều này cho phép bạn, nếu bạn muốn, sử dụng toán tử gán thuận tiện hơn thay vì lời gọi setter.


0

Bởi vì công cụ private sửa đổi truy cập hiển thị nó chỉ hiển thị trong lớp . Phương thức này vẫn ở trong lớp .


Câu hỏi của tôi là tại sao nó được thiết kế giống như "bên trong lớp" nhưng tại sao không giống như "chỉ bên trong đối tượng"?
Nageswaran

Bởi vì đó là cách java được tạo ra.
darijan

Và tại sao java lại làm java như vậy?
Nageswaran

Java đã không thành công, những người sáng tạo Java đã làm. Tại sao? Bởi vì họ đá!
darijan,

1
Người tạo Java sẽ không làm điều đó mà không có bất kỳ lý do gì và phải có lý do nào đó để làm điều này; Tôi hỏi lý do
Nageswaran

0

các privatelĩnh vực có thể truy cập vào các lớp / đối tượng, trong đó lĩnh vực này được khai báo. Nó là riêng tư đối với các lớp / đối tượng khác bên ngoài lớp / đối tượng mà nó nằm trong đó.


-1

Điều đầu tiên ở đây chúng ta phải hiểu là tất cả những gì chúng ta phải làm là phải tuân theo các nguyên tắc oops, vì vậy đóng gói được nói là bọc dữ liệu bên trong gói (tức là lớp) và đại diện cho tất cả dữ liệu dưới dạng đối tượng và dễ truy cập. vì vậy nếu chúng tôi đặt trường là không riêng tư hơn là trường được truy cập một cách riêng lẻ. và nó dẫn đến một mệnh đề xấu.


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.