Khi nào dùng Default Methods trong Java 8
Ở 2 bài trước chúng ta đã tìm hiểu 2 tính năng mới của Java 8 là Lambda Expressions và Method References (phương thức tham chiếu) trong java 8. Hôm nay chúng ta sẽ tìm hiểu thêm 1 tính năng nữa của java 8 là default methods (tạm dịch là phương thức mặc định).
1. Đặt vấn đề
Như chúng ta đã biết, Java có tính chất trừu tượng và được thể hiện thông qua abstract class và interface. Trong interface, chúng ta có thể đa kế thừa các class với nhau và thực hiện implement các phương thức đến các class khác nhau.
Bây giờ, chúng ta cùng xét một ví dụ dưới đây:
Ở ví dụ trên tôi có một interface là IA và một lớp AImpl thực hiện cài đặt các phương thức từ IA.
Bài viết này được đăng tại [free tuts .net]
package com.company.example.freetus; interface IA{ void A1(); void A2(); void A3(); } public class AImpl implements IA{ @Override public void A1() { } @Override public void A2() { } @Override public void A3() { } }
Bây giờ tôi bổ sung thêm phương thức A4 vào interface IA
void A4(){ System.out.println("A4"); }
Chương trình báo lỗi ở "void A4()" vì trong theo lý thuyết là interface chỉ chứa các phương thức không chứa xử lý logic hoặc code bên trong phương thức.
Vậy có cách nào sử dụng được phương thức A4 mà chương trình không vi phạm tính chất của interface không?
2. Default Methods là gì trong Java?
Trước khi tìm hiểu xem phương thức mặc định là gì? Chúng ta cùng xem ví dụ dưới đây:
package com.company.example.freetus; class Square { private int canh; public Square(int canh) { this.canh = canh; } public int getCanh() { return canh; } public void setCanh(int canh) { this.canh = canh; } } interface ShapeInterface{ //in ra tên hình void show(String name); //tinh dien tich float area(Square square); } class SquareImpl implements ShapeInterface{ @Override public void show(String name) { System.out.println("Ten hình: "+name); } @Override public float area(Square square) { return square.getCanh() * square.getCanh(); } } public class TestSquare { public static void main(String[] args) { Square square = new Square(5); ShapeInterface shapeInterface = new SquareImpl(); shapeInterface.show("Hình vuông"); System.out.println("Diện tích hình vuông: "+ shapeInterface.area(square)); } }
Kết quả:
- Tên hình: Hình vuông
- Diện tích hình vuông: 25.0
Chúng ta thấy class Square
sẽ thực hiện implements lại các phương thức có trong lớp ShapeInteface
đúng theo tính chất của trừu tượng trong Java.
Nhưng bây giờ nếu tôi muốn thêm một phương thức nữa vào ShapInterface mà không muốn implements vào lớp Square thì chúng ta phải làm như thế nào? Đó chính là dùng phương thức mặc định (default methods).
Phương thức mặc định là khi thêm vào một hoặc nhiều phương thức mặc định khác nhau mà không gây ảnh hưởng đến interface và các lớp liên quan đến chính lớp Interface đó.
Phương thức mặc định sẽ không bắt buộc phải implements từ interface nếu có một class đang cài đặt các phương thức từ một lớp Interface .(Nghĩa là nếu tôi có 1 lớp Interface A có chứa phương thức mặc định là B. Bây giờ tôi tạo ra một Class C thực hiện implements từ Interface A thì phương thức mặc định B của tôi sẽ không bắt buộc phải có trong Class C mà nó sẽ là một phương thức tự do).
Để sử dụng được phương thức default chúng ta sẽ thêm từ khóa “default” vào trước phương thức mà mình mong muốn phương thức đó là phương thức mặc định.
default [Kiểu trả về] [tên phương thức] { //code xử lý }
Ví dụ mẫu:
default void squareInfo(Square square){ System.out.println("Hình vuông có cạnh: "+ square.getCanh()); }
Lưu ý: Phương thức mặc định này viết giống như 1 phương thức thông thường. Nghĩa là nó có phần xử lý code trong phương thức mặc định đó.
Ví dụ code mẫu:
package com.company.example.freetus; class Square { private int canh; public Square(int canh) { this.canh = canh; } public int getCanh() { return canh; } public void setCanh(int canh) { this.canh = canh; } } interface ShapeInterface{ //Thông tin về hình vuông default void squareInfo(Square square){ System.out.println("Hình vuông có cạnh: "+ square.getCanh()); } //in ra tên hình void show(String name); //tinh dien tich float area(Square square); } class SquareImpl implements ShapeInterface{ @Override public void show(String name) { System.out.println("Tên hình: "+name); } @Override public float area(Square square) { return square.getCanh() * square.getCanh(); } } public class TestSquare { public static void main(String[] args) { Square square = new Square(5); ShapeInterface shapeInterface = new SquareImpl(); shapeInterface.squareInfo(square); shapeInterface.show("Hình vuông"); System.out.println("Diện tích hình vuông: "+ shapeInterface.area(square)); } }
Kết quả:
- Hình vuông có cạnh: 5
- Tên hình: Hình vuông
- Diện tích hình vuông: 25.0
3. Phương thức mặc định và đa kế thừa
Trong java hay trong các phiên bản trước java 8, chúng ta đã tìm hiểu và biết về đa kế thừa.
Đa kế thừa trong java là trong một lớp có thể thực hiện một hoặc nhiều interface kế thừa tới lớp mà mình muốn thực hiện. Trong java chỉ có mỗi Interface hỗ trợ đa kế thừa. Đa kế thừa chỉ thực hiện với các class và interface liên quan với nhau.
Ví dụ về đa kế thừa:
package com.company.example.freetus; interface Dog{ void run(); } interface Bird{ void fly(); } public class Animal implements Dog, Bird { @Override public void run() { } @Override public void fly() { } }
Ở ví dụ trên, con chó có thể chạy, con chim có thể bay và động vật sẽ thực hiện đa kế thừa cả 2 hoạt động của chó và mèo. Để tạo ra 1 lớp chung thực hiện 2 hoạt động của cả chó và chim.
Giờ chúng ta sẽ thực hiện thêm phương thức mặc định cho ví dụ trên. Nhưng lần này tôi sẽ thêm 1 phương thức mặc định cho cả 2 interface.
package com.company.example.freetus; interface Dog{ default void show(){ } void run(); } interface Bird{ default void show(){ } void fly(); } public class Animal implements Dog, Bird { @Override public void run() { } @Override public void fly() { } }
Lúc này ở lớp Animal sẽ báo lỗi vì có 2 phương thức có cùng 1 hành động là show().
Và lỗi này trong java thường gọi là lỗi phương thức bị xung đột.
Xung đột là một lớp có 2 hoặc nhiều nhiệm vụ và hành động giống nhau cùng được gọi đến mà bản thân lớp đó không biết xử lý như nào thì khi đó xảy ra lỗi.
Vậy làm thế nào giải quyết được vấn đề này?
Có 2 cách:
Cách đầu tiên chúng ta sẽ thực hiện Override lại phương thức show() vào lớp Animal.
public class Animal implements Dog, Bird { @Override public void show() { } ... }
Cách thứ 2 chúng ta sẽ gọi trực tiếp phương thức show()
từ một trong hai interface thông qua từ khóa “super”. Để thực hiện được trước hết chúng ta sẽ Override lại phương thức show() rồi thực hiện gọi trực tiếp phương thức show() từ Interface nào.
public class Animal implements Dog, Bird { public void show(){ Dog.super.show(); } ... }
Ở đây tôi thực hiện gọi phương thức show()
từ Interface Dog.
Dưới đây là code hoàn chỉnh của chương trình trên:
package com.company.example.freetus; interface Dog{ default void show(){ System.out.println("Đây là show Dog"); } void run(); } interface Bird{ default void show(){ System.out.println("Đây là show Bird"); } void fly(); } public class Animal implements Dog, Bird { public void show(){ Dog.super.show(); } @Override public void run() { } @Override public void fly() { } public static void main(String[] args) { Animal animal = new Animal(); animal.show(); } }
Kết quả:
- Đây là show Dog
Sau đây là những điểm quan trọng của phương thức mặc định (default method) trong java 8:
- Các interface sẽ có các phương thức mặc định với việc triển khai trong java 8 và những bản java sau này
- Các interface sẽ có thể có các phương thức tĩnh( static methods) tương tự như các phương thức tĩnh trong các lớp.
- Các phương thức mặc định cung cấp khả năng tương thích với các interface cũ đang dùng để chúng có các phương thức mới mà không ảnh hưởng đến interface đang có. Nghĩa là chúng ta có thể thêm các phương thức mặc định mà không làm ảnh hưởng đến interface và phương thức đang có cũng như là class thực hiện việc implements từ interface đó.
Trên đây là một vài khái quát cũng như là cách sử dụng phương thức mặc định được java 8 giới thiệu để sử dụng trong các Interface.
Bài tiếp theo chúng ta sẽ tìm hiểu là Optional trong Java 8.