Daily Archives: 14/07/2017

Java 8 – Interface

Trong Java thì interface (giao diện) là một kiểu dữ liệu tham chiếu tương tự như class, nhưng chỉ có thể chứa hằng số và tên các phương thức, không chứa phần thân phương thức, tuy nhiên chúng ta vẫn có thể viết phần thân phương thức cho các phương thức tĩnh và phương thức mặc định. Interface không thể được khởi tạo như lớp mà chỉ có thể được mở rộng từ các lớp khác hoặc được kế thừa từ các interface khác.

Để định nghĩa một interface thì cũng tương tự như định nghĩa một lớp:

public interface Blog {
   const String title = "phocode.com";

   void newPost();
   void deletePost();
   void updatePost();
}

Bên trong định nghĩa interface chỉ có hằng số hoặc tên phương thức, phương thức không chứa phần thân, tức là không có phần ngoặc nhọn {}, mà kết thúc bằng dấu chấm phẩy ;.

Để sử dụng interface thì phải có một lớp code lại interface đó bằng từ khóa implements, và lớp đó phải code lại toàn bộ phương thức có trong interface. Ví dụ:

public class PhoCode implements Blog {
    void newPost() {     
    }
    void deletePost {
    }
    void updatePost {
    }   
}

Trong đoạn code trên, lớp PhoCode kế thừa interface Blog, và bên trong lớp PhoCode phải code lại các phương thức có trong interface này, tức là phải có phần ngoặc nhọn {} phía sau tên phương thức.

Định nghĩa một interface

Chúng ta định nghĩa một interface bằng cách ghi từ khóa phạm vi hoạt động, rồi đến từ khóa interface, sau đó là tên của interface mà chúng ta muốn đặt, tiếp theo là danh sách các interface kế thừa nếu có, cuối cùng là phần ngoặc nhọn {} khai báo các phương thức và hằng số. Ví dụ:

public interface Blog extends Http, Database {
    String title = "phocode.com";

    void newPost();
    void deletePost();
    void updatePost();
}

Từ khóa public cho biết interface này có thể sử dụng ở bất kỳ gói nào, nếu chúng ta không khai báo từ khóa phạm vi nào thì mặc định interface này chỉ có thể được sử dụng trong cùng một gói.

Một interface có thể được kế thừa từ nhiều interface khác. Khác với class là mỗi class chỉ có thể kế thừa từ một class thì interface có thể kế thừa từ nhiều interface khác, danh sách các interface kế thừa cách nhau bởi dấu phẩy.

Các phương thức của interface có thể là phương thức trừu tượng, phương thức mặc định và phương thức tĩnh. Phương thức ảo chính là phương thức mà bình thường mà chúng ta viết, không có gì đặc biệt, phương thức mặc định thì có từ khóa default (chúng ta sẽ tìm hiểu phương thức mặc định sau), phương thức tĩnh thì có từ khóa staticTất cả các phương thức trong interface đều mặc định là public nên chúng ta không cần khai báo public.

Tất cả các hằng số trong interface là public, staticfinal.

Implement interface

Chúng ta khai báo từ khóa implements sau tên lớp rồi ghi tên danh sách các interface ra (nếu implement nhiều interface).

Lưu ý là nếu lớp này có kế thừa từ lớp khác thì từ khóa extends sẽ luôn đứng trước từ khóa implements.

Chẳng hạn như chúng ta có interface chứa phương thức so sánh xem đối tượng nào “lớn hơn” đối tượng nào như sau:

public interface Comparison {        
    public int isGreaterThan(Comparison other);
}

Sau này giả sử dụng ta có lớp Blog, và chúng ta muốn so sánh 2 đối tượng Blog với nhau thì chúng ta cho lớp Blog implements interface trên, bên trong lớp Blog chúng ta code lại phương thức isLargerThan() là xong. Tất nhiên là với điều kiện chúng ta phải có cách để so sánh 2 đối tượng, bởi vì ở đây chúng ta không so sánh 2 giá trị thô như so sánh 2 số nguyên, chẳng hạn như so sánh 2 đối tượng Blog thì so sánh xem đối tượng nào có nhiều bài viết hơn, nhiều comment hơn… v.v Ví dụ:

public class Blog implements Comparison {
    private int post_count = 0;

    int getPostCount() {
        return this.post_count;
    }

    public int isLargerThan(Comparison otherBlog) {
        if (this.getPostCount() < otherRect.getPostCount())
            return -1;
        else if (this.getPostCount() > otherRect.getPostCount())
            return 1;
        else
            return 0;               
    }
}

Trong đoạn code trên chúng ta viết lớp Blog có phương thức getPostCount() là phương thức lấy về số lượng bài viết của Blog, rồi implements interface Comparison, trong đó chúng ta code lại phương thức isLargerThan(), phương thức này nhận vào tham số là một đối tượng Blog khác, chúng ta kiểm tra xem đối tượng nào có phương thức getPostCount() lớn hơn thì trả về 1, bé hơn là -1 và bằng nhau là 0. Trên đây chỉ là một cách để chúng ta so sánh, trên thực tế chúng ta có nhiều cách khác để so sánh 2 đối tượng.

Dùng Interface làm kiểu dữ liệu

Việc định nghĩa một interface giống như định nghĩa một kiểu dữ liệu tham chiếu mới vậy. Hầu như chỗ nào chúng ta đặt tên lớp thì chúng ta cũng có thể thay bằng tên interface.

Nếu chúng ta khai báo một đối tượng có kiểu dữ liệu của một interface thì trong đoạn code khởi tạo đối tượng đó phải là khởi tạo từ một lớp có implement interface đó. Ví dụ:

public interface Blog {
    //  ...
}

public class PhoCode implements Blog {
    public static void main(String[] args) {
        Blog phocode_blog = new PhoCode();
    }
}

Mở rộng interface

Giả sử chúng ta có interface Blog với 2 phương thức mẫu như sau:

public interface Blog {
    void newPost();
    void deletePost();
}

Giả sử interface này đã được rất nhiều class implement lại, nhưng bây giờ chúng ta lại muốn thêm vào interface này một phương thức như sau:

public interface Blog {
    void newPost();
    void deletePost();
    void updatePost();
}

Và kết quả là toàn bộ các lớp có implement interface này sẽ sụp đổ vì chúng chưa code lại phương thức updatePost() mới.

Để tránh tình trạng này thì chúng ta nên đoán trước các phương thức mà chúng ta sẽ code và khai báo toàn bộ trong interface ngay từ đầu. Một cách khác là chúng ta viết một interface mới kế thừa từ interface cũ, chẳng hạn như:

public interface BlogPlus extends Blog {
    void updatePost();   
}

Và các coder sẽ có 2 lựa chọn là dùng interface cũ của bạn hoặc chuyển qua interface mới có đầy đủ các tính năng hơn.

Hoặc chúng ta có một cách khác là khai báo phương thức mặc định với từ khóa default như sau:

public interface Blog {
    void newPost();
    void deletePost();
    default updatePost() {
        // ...
    }     
}

Phương thức mặc định sẽ được code ngay bên trong interface chứ không được kế thừa nữa. Chúng ta sẽ tìm hiểu phương thức mặc định trong bài sau.