Tại sao một thành viên tư nhân có thể truy cập trong một phương thức tĩnh?


25

Sau đây là mã giả, tôi đã thử nó trong Java và PHP và cả hai đều hoạt động:

class Test { 

    private int a = 5;

    public static function do_test(){
        var t = new Test();
        t.a = 1;
        print t.a // 1
    }

}

Test::do_test();

Tại sao bạn có thể làm điều này trong mô hình OOP và việc sử dụng nó là gì?


6
Tại sao không? Trong Java, các thành viên riêng không phải là riêng tư, mà là riêng tư đối với tệp nguồn . Việc sử dụng đầu tiên đến với tâm trí là equalsphải kiểm tra các trường riêng của một trường hợp khác. (Đăng dưới dạng nhận xét, vì đây là nội dung ngắn và không có gì về tính chất OOP của phương pháp này)
14

2
Lưu ý rằng các phương thức tĩnh không có a this, vì vậy các đối tượng duy nhất của lớp mà chúng có thể có là các đối tượng chúng tự tạo (hoặc được truyền vào dưới dạng tham số). Vì vậy, nếu bạn coi đây là vi phạm đóng gói hoặc lỗ hổng bảo mật, thì nó không phải là một lỗ hổng lớn và có thể không đáng để cắm.
Kilian Foth

4
Không chắc chắn tại sao điều này đã bị hạ cấp. Câu hỏi có thể là tầm thường (ish), nhưng OP đã gặp rắc rối khi kiểm tra hành vi bằng hai ngôn ngữ trước khi hỏi. Đó là cách nỗ lực nhiều hơn chúng ta thường thấy từ những người mới đến.
yannis

1
@YannisRizos Đồng ý và thực tế tôi không nghĩ câu hỏi này là tầm thường. Nó có ý nghĩa cho việc tuân theo "nguyên tắc đặc quyền tối thiểu". Điều đó có nghĩa là các hàm trợ giúp không cần truy cập vào bên trong của một cá thể nên được xác định trong một lớp riêng biệt và ngược lại khi quy ước này được tuân theo, bạn biết rằng bất cứ khi nào một phương thức tĩnh tồn tại trong cùng một lớp, nó sẽ truy cập trạng thái bên trong.
Doval

1
Trong thực tế, khi tôi hỏi các đồng nghiệp của tôi, tất cả họ đều nói rằng điều này là không thể. Đó là lý do tại sao tôi không nghĩ nó tầm thường
Ben

Câu trả lời:


17

Trong Java, các biến riêng tư được hiển thị cho cả lớp. Chúng có thể được truy cập từ các phương thức tĩnh và từ các thể hiện khác của cùng một lớp.

Điều này, ví dụ, hữu ích trong các phương pháp nhà máy . Một phương thức xuất xưởng thường thực hiện khởi tạo cho một đối tượng phức tạp đến mức bạn không muốn để chúng vào mã ứng dụng. Để thực hiện khởi tạo, phương thức xuất xưởng thường cần quyền truy cập vào các phần bên trong lớp mà bạn không muốn phơi bày. Có thể truy cập các biến riêng tư trực tiếp làm cho cuộc sống của bạn dễ dàng hơn nhiều.

Tuy nhiên, khi bạn muốn ẩn chi tiết triển khai của một lớp ngay cả với các phương thức tĩnh hoặc từ các phiên bản khác của lớp đó, bạn có thể theo mẫu dữ liệu của lớp riêng . Đặt tất cả các biến riêng của một lớp vào một lớp bên trong riêng và ủy thác bất kỳ getters hoặc setters nào cho getters và setters của lớp bên trong đó.

Một tùy chọn khác là định nghĩa một giao diện cho lớp khai báo tất cả các phương thức chung của lớp và sau đó chỉ tham chiếu lớp dưới giao diện đó bất cứ khi nào có thể. Tham chiếu đến loại giao diện không thể được sử dụng để truy cập trực tiếp vào bất cứ thứ gì không được khai báo trong giao diện, bất kể ở đâu (ngoại trừ sự phản chiếu, tất nhiên). Khi bạn sử dụng ngôn ngữ lập trình hướng đối tượng không có giao diện (ví dụ như C ++), chúng có thể được mô phỏng với một lớp cơ sở trừu tượng được kế thừa bởi lớp thực tế.

interface ITest {
     public int getA();
}

class Test implements ITest { 

    private int a = 5;

    public int getA() { return a; } // implementation of method declared in interface

    public static void main(){
        ITest t = new Test();
        t.a = 1; // syntax error: Interface ITest has no "a"
        System.out.println(t.getA()); // calls Test.getA, visible because ITest declares it
    }

}

Bạn có thể nghĩ về một tình huống khi mẫu dữ liệu lớp riêng là hữu ích? Cá nhân tôi chỉ sử dụng các lớp bên trong trong GUI như các thiết lập (Xoay, v.v.) hoặc các lớp bên trong tĩnh trong các bài tập mã hóa vì tôi không muốn một bài tập mở rộng nhiều tệp nguồn.
Được thông báo vào

1
Việc ẩn nội bộ của một lớp khỏi các trường hợp khác sẽ lấy đi một trong những lợi thế của các lớp có giao diện mà không lấy lại được gì. Một giải pháp đơn giản và linh hoạt hơn là chỉ sử dụng một giao diện.
Doval

3

Một số ngôn ngữ và khung thời gian chạy (ví dụ Java, .NET) đưa ra giả định rằng bất kỳ ai đang biên dịch mã cho một lớp cụ thể đều có thể tin tưởng không sử dụng bất kỳ thành viên riêng nào của bất kỳ trường hợp nào của lớp đó theo cách gây bất lợi cho chính xác của nó hoạt động. Các ngôn ngữ và khung khác hạn chế hơn về vấn đề đó và không cho phép truy cập vào các thành viên riêng của một cá thể trừ mã chạy trên thể hiện đó . Cả hai thiết kế đều có ưu điểm và nhược điểm.

Ưu điểm lớn nhất của việc cho phép bất kỳ mã nào trong một lớp truy cập các thành viên riêng của bất kỳ trường hợp nào là có những trường hợp mức truy cập đó phù hợp và có privatecông việc theo cách đó giúp loại bỏ sự cần thiết phải có một vòng loại truy cập khác cho mục đích đó hoặc mặt khác, buộc mã phải phơi bày các thành viên rộng hơn so với lý tưởng.

Một lợi thế của việc không cho phép truy cập như vậy (như trường hợp trong Mô hình đối tượng chung của Microsoft (COM)) là nó cho phép mã bên ngoài coi các lớp là giao diện. Nếu một lớp ImmutableMatrixcó chứa một private hay protected double[][]lĩnh vực ủng hộ, và nếu mã trong lớp xem xét các mảng ủng hộ các trường hợp khác, sau đó nó sẽ không thể định nghĩa một lớp không cho mảng hậu thuẫn (ví dụ ZeroMatrix, IdentityMatrix) mà mã bên ngoài có thể sử dụng như một Immutable2dMatrix, không có lớp đó phải bao gồm các trường sao lưu. Nếu không có gì bên trong Immutable2dMatrixsử dụng các thành viên riêng của bất kỳ trường hợp nào khác this, thì có thể đổi tên lớp thành ImmutableArrayBackedMatrixvà định nghĩa một ImmutableMatrixlớp trừu tượng mới có thể có ImmutableArrayBackedMatrixcũng như các lớp không được hỗ trợ đã nói ở trên như các kiểu con.

Lưu ý rằng việc tái cấu trúc như vậy sẽ không được ngăn chặn bằng cách cho ngôn ngữ "cho phép" ImmutableMatrixkiểm tra mảng sao lưu cho các trường hợp khác this, trừ khi ngôn ngữ đã tận dụng khả năng đó và thực sự đã kiểm tra các trường hợp bên ngoài. Tác động chính của việc có một ngôn ngữ hạn chế việc sử dụng như vậy là nó sẽ khiến trình biên dịch vặn vẹo ngay lập tức ở bất kỳ nỗ lực nào để viết mã mà không thể chấp nhận được khi tái cấu trúc như vậy.


2

Java không hoàn toàn là một ngôn ngữ hướng đối tượng, mà là ngôn ngữ dựa trên lớp - lớp xác định các hoạt động và truy cập hành vi chứ không phải là cá thể.

Vì vậy, đừng quá ngạc nhiên khi nó cho phép bạn làm những việc không hoàn toàn hướng đối tượng.

Vì phương thức này có cùng phạm vi lớp với thể hiện, nên nó có toàn quyền truy cập vào các thành viên riêng. Các quy tắc tương tự chi phối các thể hiện của các lớp bên trong truy cập dữ liệu từ các thể hiện của các lớp bên ngoài - một thể hiện của một lớp bên trong có thể truy cập các thành viên riêng của lớp bên ngoài.

Điều này được kế thừa từ C ++, nơi nó hữu ích cho việc tạo các bản sao và di chuyển các hàm tạo. Nó cũng hữu ích để so sánh hoặc kết hợp hai đối tượng trong đó giá trị của chúng phụ thuộc vào các thành viên không thể truy cập công khai theo cách hiệu quả (ví dụ: getter cho một mảng trong Java nên sao chép mảng để mã máy khách có thể sửa đổi nó, thay đổi trạng thái bên trong của đối tượng, nhưng phải sao chép mảng để so sánh đẳng thức đối tượng không hiệu quả)

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.