Tại sao một lớp trừu tượng thực hiện một giao diện có thể bỏ lỡ việc khai báo / thực hiện một trong các phương thức của giao diện?


123

Một điều tò mò xảy ra trong Java khi bạn sử dụng một lớp trừu tượng để triển khai một giao diện: một số phương thức của giao diện có thể bị thiếu hoàn toàn (nghĩa là không có khai báo trừu tượng hoặc triển khai thực tế), nhưng trình biên dịch không phàn nàn.

Ví dụ: đưa ra giao diện:

public interface IAnything {
  void m1();
  void m2();
  void m3();
}

lớp trừu tượng sau đây được biên dịch vui vẻ mà không có cảnh báo hoặc lỗi:

public abstract class AbstractThing implements IAnything {
  public void m1() {}
  public void m3() {}
}

Bạn có thể giải thích lý do tại sao?


2
Người ta không thể tạo một đối tượng của một lớp trừu tượng. Vì vậy, miễn là việc triển khai không được cung cấp cho một lớp trừu tượng, các đối tượng không thể được tạo cho IAnything. Vì vậy, điều này là hoàn toàn tốt cho trình biên dịch. Trình biên dịch hy vọng rằng, bất kỳ lớp không trừu tượng nào thực hiện IAnything đều phải thực hiện tất cả các phương thức được khai báo từ IAnything. Và vì người ta phải mở rộng và triển khai AbstractThing để có thể tạo các đối tượng, trình biên dịch sẽ đưa ra một lỗi, nếu việc triển khai đó không thực hiện các phương thức của IAnything còn lại bởi AbstractThing.
VanagaS

Tôi đã có một lớp cụ thể đang mở rộng "Tóm tắt" của chính nó trong một kịch bản giống hệt với kịch bản này, và mặc dù tôi đã không thực hiện một trong các phương thức trong giao diện, nhưng nó không thể biên dịch được. Bây giờ nó đang làm những gì tôi mong đợi, nhưng tôi không thể tìm ra điều gì đã khiến nó thành công trước đó. Tôi nghi ngờ tôi đã không phải :wlà một trong những tập tin.
Braden hay nhất

bạn có thể thấy anwer cho một câu hỏi stackoverflow.com/questions/8026580/ trên
Do Nhu Vy

Câu trả lời:


155

Đó là bởi vì nếu một lớp là trừu tượng, thì theo định nghĩa, bạn được yêu cầu tạo các lớp con của nó để khởi tạo. Các lớp con sẽ được yêu cầu (bởi trình biên dịch) để thực hiện bất kỳ phương thức giao diện nào mà lớp trừu tượng bỏ qua.

Theo mã ví dụ của bạn, hãy thử tạo một lớp con AbstractThingmà không thực hiện m2phương thức và xem trình biên dịch đưa ra lỗi gì. Nó sẽ buộc bạn phải thực hiện phương pháp này.


1
Tôi nghĩ trình biên dịch vẫn nên đưa ra các cảnh báo liên quan đến các lớp trừu tượng triển khai các giao diện không hoàn chỉnh, đơn giản vì sau đó bạn cần xem qua 2 định nghĩa lớp thay vì 1 để xem bạn cần gì trong một lớp con. Đây là một giới hạn ngôn ngữ / trình biên dịch mặc dù.
workmad3

3
Đó sẽ không phải là một ý tưởng tốt, vì thông thường có thể có rất nhiều lớp trừu tượng và các cảnh báo 'sai' sẽ sớm áp đảo bạn, khiến bạn bỏ lỡ các cảnh báo 'đúng'. Nếu bạn nghĩ về nó, từ khóa 'trừu tượng' sẽ có một cách cụ thể để báo cho trình biên dịch thay thế các cảnh báo cho lớp đó.
belugabob

4
@workmad - nếu bạn có triển khai phổ biến cho một tập hợp con của các phương pháp giao diện nó làm cho tinh thần tốt hơn để yếu tố nó thành một lớp cơ sở riêng biệt (DRY Trumps một chỗ-code)
Gishu

4
Sẽ rất nguy hiểm khi yêu cầu bạn đặt các triển khai phương thức trống trong một lớp trừu tượng. Nếu bạn đã làm điều đó thì những người triển khai các lớp con sẽ thừa hưởng hành vi phi hành vi này mà không có trình biên dịch cho họ biết có vấn đề.
Lập hóa đơn cho thằn lằn

8
Tôi nghĩ những gì workmad có thể gợi ý là bạn định nghĩa các phương thức trong lớp trừu tượng mà không có phần thân phương thức và đánh dấu chúng là trừu tượng. Có vẻ như không phải là một ý tưởng tồi đối với tôi.
Dónal

33

Hoàn toàn ổn.
Bạn không thể khởi tạo các lớp trừu tượng .. nhưng các lớp trừu tượng có thể được sử dụng để chứa các triển khai chung cho m1 () và m3 ().
Vì vậy, nếu m2 () thực hiện khác nhau cho mỗi lần thực hiện nhưng m1 và m3 thì không. Bạn có thể tạo các triển khai IAnything cụ thể khác nhau chỉ bằng cách triển khai m2 khác nhau và xuất phát từ Tóm tắt - tôn vinh nguyên tắc DRY. Xác thực nếu giao diện được thực hiện hoàn toàn cho một lớp trừu tượng là vô ích ..

Cập nhật : Thật thú vị, tôi thấy rằng C # thực thi điều này như một lỗi biên dịch. Bạn buộc phải sao chép chữ ký phương thức và tiền tố chúng với 'công khai trừu tượng' trong lớp cơ sở trừu tượng trong kịch bản này .. (một cái gì đó mới hàng ngày :)


7

Tốt rồi. Để hiểu những điều trên, trước tiên bạn phải hiểu bản chất của các lớp trừu tượng. Chúng tương tự như các giao diện trong khía cạnh đó. Đây là những gì Oracle nói về điều này ở đây .

Các lớp trừu tượng tương tự như giao diện. Bạn không thể khởi tạo chúng và chúng có thể chứa hỗn hợp các phương thức được khai báo có hoặc không có triển khai.

Vì vậy, bạn phải suy nghĩ về những gì xảy ra khi một giao diện mở rộng giao diện khác. Ví dụ ...

//Filename: Sports.java
public interface Sports
{
   public void setHomeTeam(String name);
   public void setVisitingTeam(String name);
}

//Filename: Football.java
public interface Football extends Sports
{
   public void homeTeamScored(int points);
   public void visitingTeamScored(int points);
   public void endOfQuarter(int quarter);
}

... như bạn có thể thấy, điều này cũng biên dịch hoàn toàn tốt. Đơn giản là vì, giống như một lớp trừu tượng, một giao diện KHÔNG thể được khởi tạo. Vì vậy, không bắt buộc phải đề cập rõ ràng đến các phương thức từ "cha mẹ" của nó. Tuy nhiên, TẤT CẢ các chữ ký phương thức cha DO hoàn toàn trở thành một phần của giao diện mở rộng hoặc triển khai lớp trừu tượng. Vì vậy, một khi một lớp thích hợp (một lớp có thể được khởi tạo) mở rộng ở trên, nó sẽ được yêu cầu để đảm bảo rằng mọi phương thức trừu tượng duy nhất được thực hiện.

Hy vọng điều đó sẽ giúp ... và Allahu 'alam!


Đó là một quan điểm thú vị. Nó khiến tôi nghĩ rằng "các lớp trừu tượng" thực sự là "giao diện cụ thể", tức là giao diện với một số phương thức cụ thể, thay vì các lớp với một số phương thức trừu tượng.
Giulio Piancastelli

... Một chút của cả hai thực sự. Nhưng một điều chắc chắn, chúng không thể thực hiện được.
Biết ơn

4

Giao diện có nghĩa là một lớp không có triển khai phương thức của nó, nhưng chỉ cần khai báo.
Mặt khác, lớp trừu tượng là một lớp có thể thực hiện một số phương thức cùng với một số phương thức chỉ cần khai báo, không thực hiện.
Khi chúng ta thực hiện một giao diện cho một lớp trừu tượng, điều đó có nghĩa là lớp trừu tượng được kế thừa tất cả các phương thức của giao diện. Vì, không quan trọng để thực hiện tất cả các phương thức trong lớp trừu tượng, tuy nhiên nó thuộc về lớp trừu tượng (do thừa kế quá), vì vậy lớp trừu tượng có thể để lại một số phương thức trong giao diện mà không cần thực hiện ở đây. Nhưng, khi lớp trừu tượng này sẽ được kế thừa bởi một số lớp cụ thể, chúng phải thực hiện tất cả các phương thức chưa được thực hiện ở đó trong lớp trừu tượng.


4

Cho giao diện:

public interface IAnything {
  int i;
  void m1();
  void m2();
  void m3();
}

Đây là cách Java thực sự nhìn thấy nó:

public interface IAnything {
  public static final int i;
  public abstract void m1();
  public abstract void m2();
  public abstract void m3();
}

Vì vậy, bạn có thể để một số (hoặc tất cả) các abstractphương thức này không được thực hiện, giống như bạn sẽ làm trong trường hợp các abstractlớp mở rộng một abstractlớp khác .

Khi bạn implementan interface, quy tắc rằng tất cả các interfacephương thức phải được thực hiện trong dẫn xuất class, chỉ áp dụng cho classtriển khai cụ thể (nghĩa là không phải abstractchính nó).

Nếu bạn thực sự có kế hoạch tạo ra một cái abstract classtừ đó, thì không có quy tắc nào nói rằng bạn phải thực hiện implementtất cả các interfacephương thức (lưu ý rằng trong trường hợp như vậy, bắt buộc phải khai báo dẫn xuất classabstract)


Sử dụng javap IAnything.classđể tạo đoạn mã thứ hai.
sharhp

3

Khi một lớp trừu tượng triển khai một giao diện

Trong phần Giao diện, đã lưu ý rằng một lớp thực hiện giao diện phải thực hiện tất cả các phương thức của giao diện. Tuy nhiên, có thể định nghĩa một lớp không triển khai tất cả các phương thức của giao diện, miễn là lớp đó được khai báo là trừu tượng. Ví dụ,

abstract class X implements Y {   
    // implements all but one method of Y
}

class XX extends X {   
    // implements the remaining method in Y 
} 

Trong trường hợp này, lớp X phải trừu tượng vì nó không thực hiện đầy đủ Y, nhưng trên thực tế, lớp XX thực hiện Y.

Tham khảo: http://docs.oracle.com/javase/tutorial/java/IandI/abauge.html


1

Các lớp trừu tượng không bắt buộc phải thực hiện các phương thức. Vì vậy, mặc dù nó thực hiện một giao diện, các phương thức trừu tượng của giao diện có thể vẫn trừu tượng. Nếu bạn cố gắng thực hiện một giao diện trong một lớp cụ thể (nghĩa là không trừu tượng) và bạn không thực hiện các phương thức trừu tượng, trình biên dịch sẽ cho bạn biết: Hoặc thực hiện các phương thức trừu tượng hoặc khai báo lớp là trừu tượng.

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.