Được bảo vệ trong các giao diện


111

Tại sao tất cả các phương thức trong một interfaceđịnh nghĩa đều là ẩn public? Tại sao nó không cho phép một protectedphương pháp?


22
Câu hỏi rất hay. Đối với hầu hết mọi thứ khác trong Java, tôi đã tìm thấy lý do thực sự cho những lựa chọn được đưa ra, nhưng đối với điều này thì tôi không. Tôi hoàn toàn hợp lý khi định nghĩa một phương thức được bảo vệ trong một giao diện cho phép một lớp khác bên trong cùng một gói sử dụng phương thức này trên một đối tượng triển khai mà không yêu cầu để lộ phương thức đó, phương thức này có thể không được gọi bởi bất kỳ ai khác ngoài gói thành viên, đến phần còn lại của thế giới.
Markus A.

4
@MarkusA. Nhưng các giao diện hoạt động hai chiều, tức là chúng cũng có thể được thực hiện bởi các lớp bên ngoài gói hiện tại (và sau đó có thể được truyền dưới dạng đối số cho các phương thức bên trong gói này). Làm thế nào để một lớp từ bên ngoài gói hiện tại có thể triển khai các phương thức "được bảo vệ" của một số giao diện chung?
MartinStettner

8
@MartinStettner: Nó sẽ không. Đó sẽ là vấn đề. Một gói có thể có nhiều lớp không liên quan triển khai một giao diện và muốn đảm bảo với bất kỳ mã nào nhận được tham chiếu của loại giao diện đó rằng nó sẽ hoạt động theo một cách nhất định. Sự đảm bảo như vậy có thể được thực hiện mạnh mẽ hơn nhiều nếu mã bên ngoài có thể được ngăn chặn tuyên bố triển khai giao diện trong khi hoạt động theo cách trái với hợp đồng của nó.
supercat

1
@MarkusA. bạn nêu ra một điểm tốt, bạn sẽ có thể để đạt được nó với hệ thống mô-đun Java 9 của
Nir Alfasi

1
nếu giao diện có protectedphương thức, tất cả các lớp thực thi sẽ được xem như một kiểu con của giao diện. và tất cả các lớp này CÓ THỂ truy cập các phương thức được bảo vệ. nó không làm cho protectedtừ khóa trên phương pháp vô dụng? Miễn là chúng tôi không có bất kỳ cách nào để hạn chế người triển khai từ khóa được bảo vệ giao diện này trên phương thức là vô ích. sửa cho tôi nếu tôi sai!
Amarnath Harish

Câu trả lời:


67

Bởi vì một giao diện được cho là có nghĩa là "những gì bạn có thể nhìn thấy từ bên ngoài lớp". Sẽ không có ý nghĩa nếu thêm các phương thức không công khai.


16
Nhưng tại sao không có các chức năng mà chỉ các thành viên của cùng một gói như giao diện "có thể nhìn thấy từ bên ngoài lớp"? Tôi đã có một số trường hợp sử dụng mà tôi đã mong muốn điều này.
Markus A.

5
@MarkusA. Tôi nhận ra điều này là muộn, nhưng sau đó bạn có thể thực hiện đầy đủ abstract class, đó là tất cả interfacevà chỉ định bất kỳ quyền truy cập nào bạn muốn. Được cho là điều này làm mất đi lợi ích của nhiều triển khai interfacecó trong Java, nhưng thành thật mà nói việc thiết lập một hợp đồng tuân theo các giới hạn của một số gói khác sẽ không thể xác minh được và khó hiểu, vì thực tế bạn sẽ không thể truy cập vào phương thức triển khai của riêng mình bên ngoài gói đó .
kén chọn vào

10
@pickypg Nhưng nếu lớp sẽ triển khai giao diện đã mở rộng một lớp khác, bạn không thể mở rộng lớp khác. Tôi sẽ không thấy khó hiểu đối với một giao diện chỉ được sử dụng bên trong một gói.
Flamma

24
@Raveline, -1, Điều này đặt ra câu hỏi "tại sao một giao diện được cho là có nghĩa là những gì bạn có thể nhìn thấy từ bên ngoài lớp?" Java 8 đã cho phép thân phương thức trong các giao diện, vậy tại sao lại không cho phép các phương thức trừu tượng được bảo vệ?
Pacerier

8
Tôi thường đọc lời giải thích này, nhưng nó sai IMHO. Giao diện là một loại "giao thức trao đổi tiêu chuẩn", bất kể là nói về OOP, API giao tiếp hay phần cứng. Cổng USB trên PC của tôi rõ ràng là một giao diện công cộng. Nhưng các chân cắm trên bo mạch chủ của tôi, nằm sau một hộp khóa phím, cung cấp quyền truy cập vào các cổng USB tùy chọn rõ ràng là một giao diện được "bảo vệ". Sau đó, chúng ta có chip BIOS - đây cũng là một giao diện tiêu chuẩn hóa, nhưng nó không bao giờ được công khai theo bất kỳ cách nào, chỉ một số công ty biết chi tiết chính xác một cách riêng tư. Vì vậy, tất nhiên, các giao diện có thể có bất kỳ khả năng hiển thị nào! Tại sao không có trong OOP?
Foo Bar

55

Mặc dù lý do thường được trích dẫn là "các giao diện xác định các API công khai", tôi nghĩ rằng đó là một sự đơn giản hóa quá mức. (Và nó cũng "có mùi" của logic vòng tròn.)

Sẽ không vô nghĩa nếu có các giao diện có hỗn hợp các công cụ sửa đổi truy cập; ví dụ một phần công khai và một phần hạn chế đối với các lớp khác trong cùng gói với giao diện. Trên thực tế, trong một số trường hợp, điều này có thể hữu ích, IMO.

Trên thực tế, tôi nghĩ rằng một phần lý do đằng sau việc làm cho các thành viên của một giao diện hoàn toàn công khai là nó làm cho ngôn ngữ Java đơn giản hơn :

  • Các thành viên giao diện công khai ngầm sẽ đơn giản hơn đối với các lập trình viên. Đã bao nhiêu lần bạn thấy mã (các lớp) trong đó các công cụ sửa đổi truy cập phương thức được chọn dường như ngẫu nhiên? Rất nhiều lập trình viên "bình thường" gặp khó khăn trong việc hiểu cách tốt nhất để quản lý ranh giới trừu tượng của Java 1 . Thêm công khai / bảo vệ / gói-riêng tư vào các giao diện làm cho chúng càng khó khăn hơn.

  • Các thành viên giao diện công khai hoàn toàn đơn giản hóa đặc tả ngôn ngữ ... và do đó nhiệm vụ dành cho người viết trình biên dịch Java và những người triển khai các API phản chiếu.

Dòng suy nghĩ rằng "các giao diện xác định các API công khai" được cho là hệ quả (hoặc đặc điểm) của quyết định thiết kế ngôn ngữ đơn giản hóa ... chứ không phải ngược lại. Nhưng trên thực tế, hai luồng suy nghĩ có lẽ đã phát triển song song trong tâm trí của các nhà thiết kế Java.

Ở bất kỳ mức độ nào, phản hồi chính thức đối với RFE trong JDK-8179193 cho thấy rõ ràng rằng nhóm thiết kế Java đã quyết định 2 rằng việc cho phép các protectedgiao diện làm tăng thêm độ phức tạp vì ít lợi ích thực sự. Kudos cho @skomisa để tìm ra bằng chứng .

Bằng chứng trong RFE giải quyết vấn đề. Đó là lý do chính thức tại sao điều đó đã không được thêm vào.


1 - Tất nhiên, các lập trình viên hàng đầu không gặp khó khăn gì với những điều này và có thể chào đón một bảng tính năng kiểm soát truy cập phong phú hơn. Nhưng, điều gì sẽ xảy ra khi mã của họ được giao cho người khác duy trì?

2 - Bạn có thể không đồng ý với quyết định của họ hoặc lý do đã nêu của họ nhưng đó là tranh luận.


21

Tôi phải nói rằng câu hỏi này đã được mở lại bằng cách giới thiệu các phương thức mặc định trong Java 8. Dự án mà tôi đang làm ngay bây giờ, tương tự như bản chất cơ sở của một giao diện, có ý định trừu tượng từ việc triển khai.

Có một số trường hợp mà tôi có thể đơn giản hóa mã của mình bằng phương pháp "được bảo vệ mặc định". Nó chỉ ra rằng điều đó không thực sự hoạt động, vì các giao diện vẫn bám vào logic Java 7. Một phương thức được bảo vệ thông thường không có ý nghĩa gì đặc biệt, vì những lý do đã đề cập ở trên; nhưng nếu một phương thức công khai mặc định yêu cầu một tài nguyên cấp thấp có khả năng sẽ không thay đổi và có thể được cung cấp bởi một phương thức được bảo vệ, thì đối với tôi, có vẻ như việc có công việc "được bảo vệ mặc định" sẽ không chỉ duy trì mã sạch hơn mà còn bảo vệ người dùng trong tương lai khỏi lạm dụng tình cờ.

(Điều đáng tiếc là điều này không làm thay đổi thực tế là tôi vẫn cần phải làm phức tạp quá mức mã của mình với các phần tóm tắt không cần thiết; nhưng tôi có ý định đưa một yêu cầu tính năng vào Oracle.)


Tôi đồng ý 100%. Các lớp trừu tượng là một sự thay thế hợp lý trước khi ra đời các phương thức mặc định. Tuy nhiên, những hạn chế mà chúng đặt ra trên đa kế thừa có nghĩa là chúng không hoàn hảo. Giao diện với các phương thức mặc định thường là lựa chọn thay thế tốt hơn trong thế giới> = JDK 1.8, nhưng vì chúng không thể lưu trữ trạng thái nên chúng dựa vào việc xác định các phương thức trừu tượng khác để hiển thị trạng thái, có nghĩa là trạng thái được công khai, điều này không phải lúc nào bạn muốn.
Cha Jeremy Krieg

10

Bởi vì các giao diện xác định các API công khai. Bất cứ thứ gì được bảo vệ là một chi tiết nội bộ không thuộc về giao diện.

Bạn có thể sử dụng các lớp trừu tượng với các phương thức trừu tượng được bảo vệ, nhưng các giao diện bị hạn chế đối với các phương thức chung và trường cuối cùng tĩnh công khai.


5
Bạn đã nêu "bởi vì các giao diện xác định các API công khai". Vậy lý do gì mà các giao diện chỉ nên xác định publiccác API? Có sự khác biệt giữa "chi tiết nội bộ" với "chi tiết triển khai", protectedtrong Java chắc chắn không phải là chi tiết nội bộ vì giờ đây nó là một giao diện công khai được xuất bản cho tất cả những người có thể phân lớp nó, về cơ bản là toàn thế giới.
Pacerier

1
Không còn đúng với các phương thức mặc định của Java 8.
Mario Rossi

@MarioRossi Và cũng không còn đúng nữa với các phương thức giao diện riêng của Java 9.
skomisa

7

Có lẽ, bởi vì nó là một giao diện , tức là nó ở đó để cho khách hàng biết họ có thể làm gì với các phiên bản, thay vì nói cho họ biết những gì họ không thể làm.


1
Tôi không hiểu tại sao một giao diện cũng không thể cho các lớp con biết chúng có thể làm gì mà không để lộ việc triển khai đó ra thế giới bên ngoài. Đây là một quyết định thiết kế được đưa ra vào thời điểm mà các lớp trừu tượng có thể được sử dụng như một sự thay thế hoàn hảo. Nhưng với sự ra đời của các phương thức mặc định trong giao diện, các lớp trừu tượng hiện là một sự thay thế không hoàn hảo.
Cha Jeremy Krieg

6

Tôi mạnh mẽ cảm thấy rằng giao diện nên cho phép các phương pháp bảo vệ; ai nói rằng các giao diện phải hiển thị cho tất cả mọi người trên toàn thế giới? Theo quan điểm của bạn, nó có thể gây nhầm lẫn cho các lập trình viên "bình thường" (đọc là: không đủ năng lực): Phần lớn OOP là về cấu trúc đúng các đối tượng, lớp, gói, v.v., nếu một lập trình viên gặp khó khăn trong việc thực hiện tất cả những điều đó đúng cách, anh ta có vấn đề lớn hơn nhiều. Java được xây dựng cho loại điều đó.


Điều này không trả lời câu hỏi.
Stephen C

5

Một số câu trả lời ở đây sử dụng lý luận vòng tròn để giải thích tại sao các phương thức giao diện không thể được bảo vệ: đó là bởi vì chúng phải ở chế độ công khai, nên rõ ràng là chúng không thể được bảo vệ!

Điều đó không giải thích được gì, nhưng may mắn thay, ai đó đã đưa ra yêu cầu nâng cao cho các phương thức được bảo vệ trong giao diện như một lỗi JDK vài năm trước, điều này làm sáng tỏ vấn đề:

Các phương pháp được bảo vệ trong giao diện: chia sẻ trên các gói

Vì các công cụ sửa đổi bị hạn chế một chút trong Java, cách chia sẻ các phương thức giữa các gói bị hạn chế đối với các phương thức công khai. Đôi khi việc công khai một phương thức là rất nguy hiểm, nhưng điều đó cần phải xảy ra vì thiếu công cụ sửa đổi thích hợp. Giải pháp của tôi khắc phục được hạn chế này.

Đặc tả ngôn ngữ java hiện không cho phép sửa đổi được bảo vệ cho các phương thức giao diện. Chúng tôi có thể tận dụng thực tế này và sử dụng các phương thức giao diện được bảo vệ cho tính năng mới này.

Nếu một phương thức giao diện được đánh dấu là bảo vệ và giao diện được thực hiện bởi một lớp trong gói khác, thì phương thức đó sẽ không cần công khai, nhưng cũng có thể là riêng tư hoặc ít nhất là gói được bảo vệ. Phương thức này có thể nhìn thấy được, những gì lớp khai báo nó sẽ hiển thị và có thể nhìn thấy thêm trong gói nguồn của giao diện (và các gói con?).

Bằng cách này, chúng tôi có thể chia sẻ các phương pháp nhất định trên các gói nổi tiếng.

Và đây là phản hồi cho yêu cầu nâng cao đó, đã bị đóng lại với trạng thái Won't fix:

Đề xuất này cố gắng giải quyết một vấn đề theo cách làm tăng thêm độ phức tạp và các trường hợp đặc biệt để đạt được ít lợi ích thực tế. Một cách điển hình để giải quyết vấn đề này là có một lớp riêng thực hiện giao diện công khai. Các phương thức triển khai là công khai, nhưng nằm trong một lớp riêng tư, vì vậy chúng vẫn là riêng tư.

Một giải pháp thay thế có sẵn bắt đầu từ Java 9 là đặt các lớp và phương thức ở chế độ công khai, nhưng trong một mô-đun có khả năng xuất đủ điều kiện sang các mô-đun "bạn bè" cụ thể thay vì được xuất ra công chúng.

Vì vậy, các bài học có thẩm quyền từ báo cáo lỗi đó là:

  • Tình hình hiện tại sẽ không thay đổi; giao diện không bao giờ hỗ trợ protectedcác phương pháp.
  • Lý do cho việc không hỗ trợ protectedcác phương thức trong các giao diện là nó " làm tăng thêm độ phức tạp và các trường hợp đặc biệt để đạt được ít thực tế ".
  • Kể từ Java 9, có một cách tiếp cận thay thế để cung cấp quyền truy cập cấp độ gói vào các phương thức. Sử dụng Hệ thống mô-đun nền tảng Java (JPMS) để " đặt các lớp và phương thức ở chế độ công khai, nhưng trong một mô-đun có đủ điều kiện xuất sang các mô-đun" bạn bè "cụ thể thay vì được xuất ra công chúng ".

4

Vì một lớp triển khai phải triển khai TẤT CẢ các phương thức được khai báo trong giao diện của bạn, điều gì sẽ xảy ra nếu lớp triển khai của bạn nằm trong một gói khác?


2

Giao diện Nếu bạn muốn sử dụng thứ gì đó như bạn đã mô tả, hãy tiếp tục với các lớp trừu tượng hoặc giao diện lồng nhau.

Một đoạn trích từ Kiểu mã về các biến giao diện, nhưng vẫn áp dụng cho các phương thức:

Các biến giao diện được công khai hoàn toàn bởi vì các giao diện nhằm cung cấp Giao diện lập trình ứng dụng (API) mà các lập trình viên Java hoàn toàn có thể truy cập được để tham khảo và triển khai trong các ứng dụng của riêng họ. Vì một giao diện có thể được sử dụng trong các gói Java khác với giao diện của chúng, nên khả năng hiển thị công khai đảm bảo rằng mã chương trình có thể truy cập vào biến.

2

Khai báo các giao diện con bên trong là một phương pháp hay, nhưng protectedvề mặt kỹ thuật , bạn không thể khai báo các phương thức nội bộ của mình như trong một giao diện trong Java.

Tất nhiên, bạn có thể tạo một giao diện khác để sử dụng nội bộ, mở rộng giao diện công khai:

package yourpackage;

public interface PublicInterface {

    public void doThing1();

    public void doThing2();

    public void doThing3();

}

package yourpackage;

interface InternalInterface extends PublicInterface {

    void doAnyInternalThing1();

    void doAnyInternalThing2();

}

Bạn có thể sử dụng InternalInterfacegiao diện bên trong gói, nhưng bạn nên chấp nhận bất kỳ loại con nào của PublicInterface(trong các phương thức công khai):

package yourpackage;

public class SomeClass {

    public void someMethod(PublicInterface param) {
        if (param instanceof InternalInterface) {
            // run the optimized code
        } else {
            // run the general code
        }
    }

}

Bên ngoài gói người dùng có thể sử dụng PublicInterfacemà không gặp vấn đề gì.

Thông thường các lập trình viên tạo các lớp trừu tượng trong các tình huống tương tự. Tuy nhiên, trong trường hợp này chúng ta mất quyền lợi của đa thừa kế.


1
Bên ngoài gói người dùng cũng có thể sử dụng YourPublicInterface.Internal. Mọi thứ trong một giao diện, bao gồm cả các giao diện lồng nhau, là công khai bất kể sự hiện diện hay vắng mặt của publictừ khóa.
Greg Roelofs

1

Tình huống duy nhất mà nó sẽ có ý nghĩa là khi bạn muốn hạn chế khả năng hiển thị trong cùng một gói. Tất cả các mục đích sử dụng khác protectedkhông được áp dụng. Cụ thể, protectedcác phương thức thường được sử dụng để cung cấp quyền truy cập vào một số chi tiết của các triển khai cấp thấp hơn cho con cháu. Nhưng tuyên bố điều đó trong một giao diện không có ý nghĩa, vì không có triển khai cấp thấp hơn để hiển thị.

Và ngay cả kịch bản gói không thực sự là giao diện.

Để đạt được những gì bạn có thể muốn, bạn cần hai giao diện, một để sử dụng nội bộ và một giao diện mà bạn để lộ trong API công khai. (Có thể có cái bên trong, nhưng không nhất thiết phải mở rộng cái công khai.) Hoặc, như những người khác đã chỉ ra, một lớp cha trừu tượng.


Một lớp cha trừu tượng có thể ngăn chính nó được dẫn xuất bởi các kiểu bên ngoài gói của nó. Một người nào đó nhận được một tham chiếu thuộc loại siêu lớp như vậy, người tin tưởng tác giả của gói, có thể yên tâm rằng đối tượng sẽ hoạt động như được tuyên bố. Nếu một gói có nhiều lớp muốn hiển thị một số chức năng phổ biến, nhưng không phù hợp với hệ thống phân cấp thích hợp (ví dụ: một triển khai các chức năng X và Y, một Y và Z, và một X và Z) thì sẽ rất hữu ích nếu nó có thể hiển thị chức năng sử dụng giao diện trong khi vẫn hứa hẹn rằng các phiên bản mà các loại giao diện đề cập đến sẽ là "chính hãng".
supercat

0

Các phương thức được bảo vệ luôn có thể được truy cập bởi lớp con chỉ khi lớp con mở rộng lớp cơ sở.

Trong trường hợp giao diện, lớp con không bao giờ mở rộng giao diện. Nó thực hiện giao diện.

Các phương pháp được bảo vệ có thể truy cập thông qua mở rộng và không phải thực hiện .


những gì khác biệt những gì từ khóa, nó không quan trọng.
Alex78191 18/09/19

0

Giao diện có nghĩa là để hiển thị các phương thức với thế giới bên ngoài . Vì vậy, các phương pháp này là công khai về bản chất. Tuy nhiên, nếu bạn muốn giới thiệu tính trừu tượng trong cùng một họ các lớp, bạn có thể tạo một mức độ trừu tượng khác giữa giao diện và lớp thực thi, tức là một lớp trừu tượng. Dưới đây là một ví dụ.

public interface MyInterface {
    public void publicMethod(); // needs to be public
}

public abstract class MyAbstractClass implements MyInterface {
    @Override
    public void publicMethod() {
        protectedMethod(); // you can call protected method here
        // do other stuff
    }
    protected abstract void protectedMethod(); // can be protected
}

public class MyClass extends MyAbstractClass {
    @Override
    protected void protectedMethod() {
        // implement protected method here, without exposing it as public
    }
}

2
Nhưng các phương thức riêng tư mà không ai nhìn thấy được cho phép trong các giao diện.
Alex78191 18/09/19

java có các phương thức mặc định trong các giao diện, nghĩa là, một giao diện là một phần mềm để bỏ qua tính đa kế thừa của các lớp trừu tượng. Cho phép nhiều kế thừa của các lớp trừu tượng. Xung đột của các phương pháp mặc định không trở thành vấn đề.
Alex78191 18/09/19

Bạn đúng rồi. Kể từ Java 9, các phương thức private được phép trong các giao diện. Chúng không thể trừu tượng và chúng được thực hiện và sử dụng trong giao diện, chủ yếu bằng các phương thức mặc định hoặc tĩnh khác (nếu bản thân chúng là tĩnh).
Stefanos Kargas 19/09/19

1
Câu trả lời này chỉ đơn giản là bỏ qua câu hỏi được đặt ra: tại sao các giao diện không thể có các phương thức được bảo vệ? Các phương thức được bảo vệ vẫn sẽ "đưa các phương thức ra thế giới bên ngoài" và tuyên bố rằng "các phương pháp này là công khai về bản chất" chỉ là sai. Chúng công khai vì ngôn ngữ được thiết kế theo cách đó, nhưng nó có thể cho phép các phương thức được bảo vệ trong các giao diện. OP chỉ đơn giản là hỏi tại sao.
skomisa
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.