JavaRush /Blog Java /Random-VI /Giao diện trong Java

Giao diện trong Java

Xuất bản trong nhóm
Xin chào! Hôm nay chúng ta sẽ nói về một khái niệm quan trọng trong Java - giao diện. Từ này có lẽ quen thuộc với bạn. Ví dụ: hầu hết các chương trình và trò chơi máy tính đều có giao diện. Theo nghĩa rộng, giao diện là một loại “điều khiển từ xa” kết nối hai bên tương tác với nhau. Một ví dụ đơn giản về giao diện trong cuộc sống hàng ngày là điều khiển từ xa của TV. Nó kết nối hai vật thể, một người và một TV, đồng thời thực hiện các tác vụ khác nhau: tăng hoặc giảm âm lượng, chuyển kênh, bật hoặc tắt TV. Một bên (người) cần truy cập vào giao diện (nhấn nút điều khiển từ xa) để bên kia thực hiện thao tác. Ví dụ: để TV chuyển kênh sang kênh tiếp theo. Trong trường hợp này, người dùng không cần biết thiết bị của TV và quá trình thay đổi kênh được thực hiện bên trong nó như thế nào. Tại sao cần có giao diện trong Java - 1Tất cả những gì người dùng có quyền truy cập là giao diện . Nhiệm vụ chính là để có được kết quả mong muốn. Điều này có liên quan gì đến lập trình và Java? Direct :) Tạo một giao diện rất giống với việc tạo một lớp thông thường, nhưng thay vì từ classchúng ta chỉ định từ interface. Chúng ta hãy xem giao diện Java đơn giản nhất và tìm hiểu cách nó hoạt động cũng như những gì nó cần thiết cho:
public interface Swimmable  {

     public void swim();
}
Chúng tôi đã tạo ra một giao diện Swimmablethể bơi được . Đây giống như điều khiển từ xa của chúng tôi, có một “nút”: phương pháp swim() là “bơi”. Làm thế nào chúng ta có thể sử dụng “ điều khiển từ xa ” này? Đối với mục đích này, phương pháp, tức là nút trên điều khiển từ xa của chúng tôi cần được triển khai. Để sử dụng một giao diện, các phương thức của nó phải được một số lớp trong chương trình của chúng ta triển khai. Hãy nghĩ ra một lớp có đồ vật phù hợp với mô tả “có thể bơi”. Ví dụ: lớp vịt phù hợp Duck:
public class Duck implements Swimmable {

    public void swim() {
        System.out.println("Duck, swim!");
    }

    public static void main(String[] args) {

        Duck duck = new Duck();
        duck.swim();
    }
}
Chúng ta thấy gì ở đây? Một lớp Duckđược liên kết với một giao diện Swimmablebằng từ khóa implements. Nếu bạn còn nhớ, chúng ta đã sử dụng một cơ chế tương tự để kết nối hai lớp trong tính kế thừa, chỉ có từ “ extend ”. “ public class Duck implements Swimmable” có thể dịch theo nghĩa đen cho rõ ràng: “một lớp công khai Ducktriển khai giao diện Swimmable.” Điều này có nghĩa là một lớp được liên kết với một giao diện phải triển khai tất cả các phương thức của nó. Xin lưu ý: trong lớp của chúng ta, Duckgiống như trong giao diện , Swimmablecó một phương thức swim()và bên trong nó có một loại logic nào đó. Đây là một yêu cầu bắt buộc. Nếu chúng ta chỉ viết “ public class Duck implements Swimmable” và không tạo một phương thức nào swim()trong lớp Duck, trình biên dịch sẽ báo lỗi cho chúng ta: Duck không trừu tượng và không ghi đè phương thức trừu tượng swim() trong Swimmable Tại sao điều này lại xảy ra? Nếu chúng tôi giải thích lỗi bằng ví dụ về TV, thì hóa ra là chúng tôi đang đưa cho một người một chiếc điều khiển từ xa có nút “đổi kênh” từ một chiếc TV không biết cách chuyển kênh. Lúc này, hãy nhấn nút bao nhiêu tùy thích, sẽ không có tác dụng gì. Bản thân điều khiển từ xa không chuyển kênh: nó chỉ cung cấp tín hiệu cho TV, bên trong đó thực hiện một quy trình chuyển kênh phức tạp. Với con vịt của chúng ta cũng vậy: nó phải có khả năng bơi để có thể truy cập bằng giao diện Swimmable. Nếu cô ấy không biết cách thực hiện, giao diện Swimmablesẽ không kết nối được hai bên - con người và chương trình. Một người sẽ không thể sử dụng một phương thức swim()để làm cho một đối tượng Duckbên trong chương trình nổi. Bây giờ bạn đã thấy rõ hơn giao diện dùng để làm gì. Giao diện mô tả hành vi mà các lớp triển khai giao diện đó phải có. “Hành vi” là tập hợp các phương pháp. Nếu chúng ta muốn tạo nhiều trình nhắn tin, cách dễ nhất để thực hiện việc này là tạo một giao diện Messenger. Bất kỳ người đưa tin nào cũng có thể làm được điều gì? Ở dạng đơn giản, nhận và gửi tin nhắn.
public interface Messenger{

     public void sendMessage();

     public void getMessage();
}
Và bây giờ chúng ta có thể chỉ cần tạo các lớp nhắn tin bằng cách triển khai giao diện này. Bản thân trình biên dịch sẽ “ép buộc” chúng ta triển khai chúng bên trong các lớp. Điện tín:
public class Telegram implements Messenger {

    public void sendMessage() {

        System.out.println("Sending a message to Telegram!");
    }

     public void getMessage() {
         System.out.println("Reading the message in Telegram!");
     }
}
WhatsApp:
public class WhatsApp implements Messenger {

    public void sendMessage() {

        System.out.println("Sending a WhatsApp message!");
    }

     public void getMessage() {
         System.out.println("Reading a WhatsApp message!");
     }
}
Viber:
public class Viber implements Messenger {

    public void sendMessage() {

        System.out.println("Sending a message to Viber!");
    }

     public void getMessage() {
         System.out.println("Reading a message in Viber!");
     }
}
Điều này mang lại lợi ích gì? Điều quan trọng nhất trong số đó là khớp nối lỏng lẻo. Hãy tưởng tượng rằng chúng ta đang thiết kế một chương trình trong đó chúng ta sẽ thu thập dữ liệu khách hàng. Lớp này Clientphải có một trường cho biết ứng dụng khách sử dụng trình nhắn tin nào. Nếu không có giao diện, nó sẽ trông lạ:
public class Client {

    private WhatsApp whatsApp;
    private Telegram telegram;
    private Viber viber;
}
Chúng tôi đã tạo ba trường, nhưng một khách hàng có thể dễ dàng chỉ có một trình nhắn tin. Chúng tôi chỉ không biết cái nào. Và để không bị mất liên lạc với khách hàng, bạn phải “đẩy” tất cả các phương án có thể vào lớp. Hóa ra một hoặc hai trong số chúng sẽ luôn ở đó nullvà chúng hoàn toàn không cần thiết để chương trình hoạt động. Thay vào đó, tốt hơn là sử dụng giao diện của chúng tôi:
public class Client {

    private Messenger messenger;
}
Đây là một ví dụ về “khớp nối lỏng lẻo”! Thay vì chỉ định một lớp trình nhắn tin cụ thể trong lớp Client, chúng tôi chỉ đề cập rằng khách hàng có trình nhắn tin. Cái nào sẽ được xác định trong suốt quá trình của chương trình. Nhưng tại sao chúng ta cần giao diện cho việc này? Tại sao chúng lại được thêm vào ngôn ngữ? Câu hỏi hay và đúng! Kết quả tương tự có thể đạt được bằng cách sử dụng tính kế thừa thông thường, phải không? Lớp này Messengerlà lớp cha, và Viber, TelegramWhatsApplà những người thừa kế. Thật vậy, có thể làm như vậy. Nhưng có một nhược điểm. Như bạn đã biết, Java không có tính đa kế thừa. Nhưng có nhiều cách triển khai giao diện. Một lớp có thể triển khai bao nhiêu giao diện tùy thích. Hãy tưởng tượng rằng chúng ta có một lớp Smartphonecó một trường Application- một ứng dụng được cài đặt trên điện thoại thông minh.
public class Smartphone {

    private Application application;
}
Tất nhiên, ứng dụng và trình nhắn tin là tương tự nhau, nhưng chúng vẫn là những thứ khác nhau. Messenger có thể là cả thiết bị di động và máy tính để bàn, trong khi Ứng dụng là ứng dụng di động. Vì vậy, nếu chúng ta sử dụng tính kế thừa, chúng ta sẽ không thể thêm đối tượng Telegramvào lớp Smartphone. Rốt cuộc, một lớp Telegramkhông thể kế thừa từ Applicationvà từ Messenger! Và chúng tôi đã quản lý để kế thừa nó từ Messengervà thêm nó vào lớp ở dạng này Client. Nhưng một lớp Telegramcó thể dễ dàng triển khai cả hai giao diện! Do đó, trong một lớp Clientchúng ta có thể triển khai một đối tượng Telegramdưới dạng Messenger, và trong một lớp Smartphonedưới dạng Application. Đây là cách nó được thực hiện:
public class Telegram implements Application, Messenger {

    //...methods
}

public class Client {

    private Messenger messenger;

    public Client() {
        this.messenger = new Telegram();
    }
}


public class Smartphone {

    private Application application;

    public Smartphone() {
        this.application = new Telegram();
    }
}
Bây giờ chúng ta có thể sử dụng lớp này Telegramtheo ý muốn. Ở đâu đó anh ấy sẽ đóng vai Application, ở đâu đó trong vai Messenger. Có thể bạn đã nhận thấy rằng các phương thức trong giao diện luôn “trống”, nghĩa là chúng không được triển khai. Lý do cho điều này rất đơn giản: một giao diện mô tả hành vi chứ không phải thực hiện nó. “Tất cả các đối tượng của các lớp triển khai giao diện Swimmablephải có khả năng nổi”: đó là tất cả những gì giao diện nói với chúng ta. Chính xác thì một con cá, con vịt hay con ngựa sẽ bơi như thế nào là một câu hỏi dành cho các lớp Fish, DuckHorse, chứ không phải dành cho giao diện. Cũng giống như việc chuyển kênh là nhiệm vụ của một chiếc TV. Điều khiển từ xa chỉ đơn giản cung cấp cho bạn một nút để làm điều đó. Tuy nhiên, Java8 có một bổ sung thú vị - các phương thức mặc định. Ví dụ: giao diện của bạn có 10 phương thức. 9 trong số chúng được triển khai khác nhau ở các lớp khác nhau, nhưng tất cả đều được triển khai giống nhau. Trước đây, trước khi phát hành Java8, các phương thức bên trong giao diện hoàn toàn không được triển khai: trình biên dịch ngay lập tức báo lỗi. Bây giờ bạn có thể làm điều đó như thế này:
public interface Swimmable {

   public default void swim() {
       System.out.println("Swim!");
   }

   public void eat();

   public void run();
}
Sử dụng từ khóa default, chúng tôi đã tạo một phương thức trong giao diện với cách triển khai mặc định. Chúng tôi sẽ cần triển khai hai phương thức còn lại eat()run()chính chúng tôi trong tất cả các lớp sẽ triển khai Swimmable. Không cần phải làm điều này với phương thức này swim(): việc triển khai sẽ giống nhau ở tất cả các lớp. Nhân tiện, bạn đã gặp các giao diện nhiều lần trong các nhiệm vụ trước đây, mặc dù chính bạn cũng không nhận thấy điều đó :) Đây là một ví dụ rõ ràng: Tại sao chúng ta cần giao diện trong Java - 2Bạn đã làm việc với các giao diện ListSet! Chính xác hơn, với việc triển khai chúng - ArrayList, LinkedList, HashSetvà những thứ khác. Sơ đồ tương tự cho thấy một ví dụ khi một lớp thực hiện nhiều giao diện cùng một lúc. Ví dụ, LinkedListnó thực hiện các giao diện ListDeque(hàng đợi hai mặt). Bạn cũng đã quen thuộc với giao diện Map, hay đúng hơn là với cách triển khai nó - HashMap. Nhân tiện, trong sơ đồ này, bạn có thể thấy một tính năng: các giao diện có thể được kế thừa từ nhau. Giao diện SortedMapđược kế thừa từ Map, và Dequeđược kế thừa từ queue Queue. Điều này là cần thiết nếu bạn muốn hiển thị kết nối giữa các giao diện, nhưng một giao diện là phiên bản mở rộng của giao diện khác. Hãy xem một ví dụ về giao diện Queue- hàng đợi. Chúng tôi chưa xem qua các bộ sưu tập Queue, nhưng chúng khá đơn giản và được sắp xếp giống như một hàng thông thường trong cửa hàng. Bạn chỉ có thể thêm các phần tử vào cuối hàng đợi và chỉ xóa chúng khỏi đầu hàng. Ở một giai đoạn nhất định, các nhà phát triển cần một phiên bản mở rộng của hàng đợi để có thể thêm và nhận các phần tử từ cả hai phía. Đây là cách tạo ra một giao diện Deque- hàng đợi hai chiều. Nó chứa tất cả các phương thức của một hàng đợi thông thường, bởi vì nó là “mẹ” của hàng đợi hai chiều, nhưng các phương thức mới đã được thêm vào.
Bình luận
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION