1. Khả năng

Để hiểu rõ hơn về lợi ích của giao diện và nơi sử dụng chúng, chúng ta cần nói về một số điều trừu tượng hơn.

Một lớp thường mô hình hóa một đối tượng cụ thể. Một giao diện tương ứng ít hơn với các đối tượng và nhiều hơn nữa với khả năng hoặc vai trò của chúng.

Bản chất của giao diện

Ví dụ, những thứ như ô tô, xe đạp, xe máy và bánh xe được thể hiện tốt nhất dưới dạng các lớp và đối tượng. Nhưng khả năng của họ — chẳng hạn như "Tôi có thể cưỡi", "Tôi có thể vận chuyển người", "Tôi có thể đứng" - được trình bày tốt hơn dưới dạng giao diện. Dưới đây là một số ví dụ:

Mã số Sự miêu tả
interface CanMove
{
   void move(String newLocation);
}
Tương ứng với khả năng di chuyển
interface Rideable
{
   void ride(Passenger passenger);
}
Tương ứng với khả năng được cưỡi
interface CanTransport
{
   void addStuff(Object stuff);
   Object removeStuff();
}
Tương ứng với khả năng chuyên chở đồ đạc
class Wheel implements CanMove
{
   ...
}
Lớp Wheelhọc có thể di chuyển
class Car implements CanMove, Rideable, CanTransport
{
   ...
}
Lớp Carcó thể di chuyển, được cưỡi và vận chuyển đồ đạc
class Skateboard implements CanMove, Rideable
{
   ...
}
Lớp Skateboardcó thể di chuyển và được cưỡi


2. Vai trò

Các giao diện đơn giản hóa rất nhiều cuộc sống của một lập trình viên. Thông thường, một chương trình có hàng nghìn đối tượng, hàng trăm lớp, nhưng chỉ có vài chục giao diện , tức là các vai trò . Có ít vai trò, nhưng có nhiều cách để kết hợp chúng (các lớp).

Toàn bộ vấn đề là bạn không phải viết mã cho mỗi lớp để tương tác với mọi lớp khác. Bạn chỉ cần tương tác với các vai trò (giao diện) của chúng.

Hãy tưởng tượng bạn là một người huấn luyện thú cưng. Mỗi thú cưng mà bạn làm việc cùng có thể có nhiều khả năng khác nhau. Bạn tranh cãi thân thiện với người hàng xóm về việc thú cưng của ai có thể gây ồn ào nhất. Để giải quyết vấn đề, bạn chỉ cần sắp xếp tất cả những thú cưng có thể "nói" thành hàng và bạn ra lệnh cho chúng: Nói!

Bạn không quan tâm chúng là loại động vật gì hay chúng có những khả năng gì khác. Ngay cả khi họ có thể thực hiện cú lộn ba vòng. Tại thời điểm cụ thể này, bạn chỉ quan tâm đến khả năng nói to của họ. Đây là những gì nó sẽ trông giống như trong mã:

Mã số Sự miêu tả
interface CanSpeak
{
   void speak();
}
khả CanSpeaknăng. Giao diện này hiểu lệnh to speak, nghĩa là nó có một phương thức tương ứng.
class Cat implements CanSpeak
{
   void speak()
   {
      println("MEOW");
   }
}

class Dog implements CanSpeak
{
   void speak()
   {
      println("WOOF");
   }
}

class Fish
{
   ...
}
Động vật có tính năng này.

Để dễ hiểu, chúng tôi đã cung cấp tên các lớp bằng tiếng Anh. Điều này được cho phép trong Java, nhưng nó rất không mong muốn.













Của chúng tôi Fishkhông có khả năng nói (không triển khai CanSpeakgiao diện).

public static void main(String[] args)
{
   // Add all the animals to the list
   ArrayList pets = new ArrayList();
   pets.add(new Cat());
   pets.add(new Dog());
   pets.add(new Fish());

   // If the ability exists, then make a sound
   for(Object pet: pets)
   {
      if (pet instanceof CanSpeak)
      {
         CanSpeak loudmouth = (CanSpeak) pet;
         loudmouth.speak();
      }
   }
}
Và làm thế nào để chúng tôi ra lệnh cho họ?

Khi số lớp trong chương trình của bạn lên tới hàng nghìn, bạn sẽ không thể sống thiếu giao diện. Thay vì mô tả sự tương tác của hàng nghìn lớp, chỉ cần mô tả sự tương tác của vài chục giao diện là đủ — điều này giúp đơn giản hóa cuộc sống rất nhiều.

Và khi được kết hợp với tính đa hình, cách tiếp cận này nói chung là một thành công rực rỡ.



3. defaultViệc thực hiện các phương thức giao diện

Các lớp trừu tượng có thể có các biến và triển khai các phương thức, nhưng chúng không thể có nhiều kế thừa. Các giao diện không thể có các biến hoặc triển khai các phương thức, nhưng có thể có nhiều kế thừa.

Tình hình được thể hiện trong bảng sau:

Khả năng/tài sản lớp trừu tượng giao diện
Biến
phương pháp thực hiện
đa thừa kế

Vì vậy, một số lập trình viên thực sự muốn các giao diện có khả năng triển khai phương thức. Nhưng có khả năng thêm một triển khai phương thức không có nghĩa là một phương thức sẽ luôn được thêm vào. Thêm nó nếu bạn muốn. Hoặc nếu bạn không, sau đó không.

Ngoài ra, các vấn đề với đa thừa kế chủ yếu là do các biến. Trong mọi trường hợp, đó là những gì họ đã quyết định và đã làm. Bắt đầu với JDK 8, Java đã giới thiệu khả năng thêm các triển khai phương thức vào giao diện.

Đây là bảng cập nhật (dành cho JDK 8 trở lên):

Khả năng/tài sản lớp trừu tượng giao diện
Biến
phương pháp thực hiện
đa thừa kế

Bây giờ đối với các lớp trừu tượng cũng như các giao diện, bạn có thể khai báo các phương thức có hoặc không có triển khai. Và đây là tin tuyệt vời!

Trong các lớp trừu tượng, các phương thức không có triển khai phải được đặt trước từ abstractkhóa. Bạn không cần thêm bất kỳ thứ gì trước các phương thức có triển khai. Trong các giao diện, điều ngược lại là đúng. Nếu một phương thức không có triển khai, thì không nên thêm gì. Nhưng nếu có triển khai thì defaultphải thêm từ khóa vào.

Để đơn giản, chúng tôi trình bày thông tin này trong bảng nhỏ sau:

Khả năng/tài sản lớp trừu tượng giao diện
Phương pháp không có thực hiện abstract
Phương pháp với một thực hiện default

Vấn đề

Sử dụng các giao diện có các phương thức có thể đơn giản hóa rất nhiều hệ thống phân cấp lớp lớn. Ví dụ, trừu tượng InputStreamOutputStreamcác lớp có thể được khai báo như các giao diện! Điều này cho phép chúng tôi sử dụng chúng thường xuyên hơn và thuận tiện hơn nhiều.

Nhưng đã có hàng chục triệu (tỷ?) các lớp Java trên thế giới. Và nếu bạn bắt đầu thay đổi các thư viện tiêu chuẩn, thì bạn có thể làm hỏng thứ gì đó. Như tất cả mọi thứ! 😛

Để không vô tình làm hỏng các chương trình và thư viện hiện có, người ta đã quyết định rằng việc triển khai phương thức trong các giao diện sẽ có quyền ưu tiên kế thừa thấp nhất .

Ví dụ: nếu một giao diện kế thừa một giao diện khác có một phương thức và giao diện đầu tiên khai báo cùng một phương thức nhưng không có triển khai, thì việc triển khai phương thức từ giao diện kế thừa sẽ không đến được giao diện kế thừa. Ví dụ:

interface Pet
{
   default void meow()
   {
      System.out.println("Meow");
   }
}

interface Cat extends Pet
{
   void meow(); // Here we override the default implementation by omitting an implementation
}

class Tom implements Cat
{
}

Mã sẽ không biên dịch vì Tomlớp không triển khai meow()phương thức.