Viblo CTF
+2

Giới thiệu Default method trong Java 8

Java 8 mới được ra mắt với nhiều cải tiến so với các phiên bản gần đây. Một trong những thay đổi lớn nhất là khái niệm về interface, chúng ta đều biết rằng từ phiên bản 7 về trước, java không cho phép viết định nghĩa (definition) 1 method trong interface mà chỉ cho phép miêu tả nó (declaration), nhưng đối với java 8 chúng ta sẽ có thêm 2 khái niệm mới trong interface đó là default method và static method. Bài viết này sẽ tập trung vào default method và những ứng dụng của nó.

I - Cài đặt default method

Như chúng ta đã biết trước đây java không cho phép define 1 method trong interface mà chỉ có thể declare 1 method như sau:

// a) examp 01 : java 7
public interface IDBManager{
	void read();
    void write();
}

Trong java 8 chúng ta có thể viết 1 method trong interface như sau:

// b) examp 02: java 8
public interface IDBManager{
	void read();
    void write();
    default void log(String message){
    	System.out.println("LOG: "+message);
    }
}

Ở đây chúng ta nói log(String message) là 1 default method của interface.

Nếu một interface khác thừa kế từ interface trên thì nó có 3 sự lựa chọn:

  1. Không làm gì với method log(), trong trường hợp này method log() cũng là default trong interface mới này: ví dụ
public interface IAnotherDBManager extends IDBManager{
//
}

Trong interface trên cũng có method log(message) giống hệt interface cha.

  1. Override method log() Ví dụ
public interface IAnotherDBManager extends IDBManager{
	public final int MAX_LOG_CHAR = 100;
	public default void log(String message){
    	if (message.length() > MAX_LOG_CHAR){
        	System.err.println("Message is too long!");
            return;
        }
    	System.out.println("LOG: "+message);
    }
}

Như vậy các class implement từ interface mới này sẽ được kế thừa method log mới của nó.

  1. Định nghĩa lại method log là abstract trong interface mới Ví dụ
public interface IAnotherDBManager extends IDBManager{
	public void log(String message);
}

Với lựa chọn này thì method log(message) đã trở thành abstract và tất cả các concrette classes implement từ interface này sẽ phải implement method log() này giống như bình thường.

Nếu một abstract class/concrete class implements từ interface IDBManager trên thì chỉ có 2 sự lựa chọn:

  1. Nếu không làm gì với method log(message) thì method log() sẽ được thừa kế từ interface.
  2. Có thể overriden lại method log(message) nhưng method phải ở dạng abstract hoặc bình thường (không có default method cho class) ví dụ:
public abstract class AbstractDBManager implements IDBManager{
	public abstract void log(String message);
}
public class BearDBManager implements IDBManager{
	public void log(String message){
    	System.out.println("BEAR: " + message);
    }
}

II - Mục đích

Như trong đoạn code ở trên nếu giả sử ta có 2 class implement từ interface IDBManager chẳng hạn như sau:

class BooksDBManager implements IDBManager{
	public void read(){
    //------------------
    }
    public void write(){
    //------------------
    }
}

class ImagesDBManager implement IDBManager{
	public void read(){
    //-------------------
    }
    public void write(){
    //-------------------
    }
}

giả sử trong quá trình phát triển, người ta muốn bổ sung 1 method vào interface là log(message) cho phép lưu lại tất cả các giao dịch chẳng hạn, như vậy như ở phiên bản java 7, nếu thêm 1 method vào interface thì bắt buộc phải sửa chữa tất cả các concrete classes implement từ interface đó, cụ thể ở đây là ta phải implement method log(message) trong các class BooksDBManager và ImagesDBManager như sau:

class BooksDBManager implements IDBManager{
	//-----------------
    public void log(String message){
    //-----------------
    }
}

nay với java 8 muốn thêm vào 1 method trong interface ta chỉ việc đặt từ khoá default vào như sau:

public interface IDBManager{
	//------------------------
    public default void log(String message){
    //----------------------------------------
    }
}

tất cả các classes implements từ IDBManager sẽ được kế thừa method log(message) và chúng ta không phải sửa chữa lại các classes đó. Mục đích của default method ra đời là để bổ sung thêm các method vào interface (ví dụ như khi cần thêm 1 tính năng cho thư viện) mà không phải thay đổi những implementation code trước đây (đảm bảo tính tương thích).

II - Tiện ích

Một trong những tiện ích lớn nhất của default method đó là: Sử dụng trong việc dùng Comparator với Comparator.comparing() và Comparator.thenComparing() Trước đây với phiên bản java 7 nếu muốn sử dụng Comparator cho việc sắp xếp 1 list chúng ta sẽ phải implement Comparator Interface, chẳng hạn như ở ví dụ sau:

class Human implements IAnimal{
	private int age;
    private String name;
    public void setAge(int age){
    	this.age = age;
    }
    public int getAge(){
    	return age;
    }
    public void setName(String name){
    	this.name = name;
    }
    public String getName(){
    	return name;
    }
}

chúng ta có 1 class là Human trong đó có 1 field là age và có 1 danh sách lưu trữ các đối tượng Human chẳng hạn:

ArrayList<Human> humanList = new ArrayList<Human>();
Human human1 = new Human();
human1.setName("N Van A");
human1.setAge(18);
Human human2 = new Human();
human2.setName("James");
human2.setAge(25);
Human human3 = new Human();
human3.setName("Fiora");
human3.setAge(10);
Human human4 = new Human();
human4.setName("An Khanh");
human4.setAge(10);
humanList.add(human1);
humanList.add(human2);
humanList.add(human3);
humanList.add(human4);

bây giờ muốn sắp xếp humanList theo age tăng dần, ta cần tạo 1 class SortByAge implements từ Comparator như sau:

class SortByAge implements Comparator<Human>{
	public int compare(Human o1, Human o2){
    	return o1.getAge() - o2.getAge();
    }
}

và sử dụng lớp java.util.Collections để sắp xếp:

Collections.sort(humanList, new SortByAge());

Sang đến phiên bản 8 với sự hỗ trợ của lamda expression, static method interface và default method interface, chúng ta không cần thiết phải dài dòng như vậy, chỉ cần gọi lệnh:

Collections.sort(humanList, (a, b)->a.getAge()-b.getAge());

do Comparator là 1 functional interface nên ta hoàn toàn có thể thay thế 1 đối tượng Comparator bằng 1 lamda expression như trên.

Với sự trợ giúp của static method interface chúng ta có thể viết gọn hơn nữa

Collections.sort(humanList,Comparator.comparing(Human::getAge()));
//or
humanList.sort(Comparator.comparing(Human::getAge()));

comparing là 1 static method interface của Comparator, cho phép chúng ta truyền vào 1 Function Reference, một khái niệm mới trong Java 8, 1 tham chiếu tới Function getAge trong Class Human.

Trong trường hợp nếu Age bằng nhau thì không thể so sánh, chúng ta có thể so sánh dựa trên Name, và câu lệnh bên trên có thể được "nâng cấp" như sau với sự trợ giúp của default method thenComparing():

Collections.sort(humanList, Comparator.comparing(Human::getAge()).thenComparing(Human::getName()));

Rất ngắn gọn và dễ sử dụng!

IV - Chú ý

** Về multiple inheritance**

Ví dụ chúng ta có 1 concrete class implement từ 2 interface khác nhau và 2 interface đó có 2 default method có tên giống nhau, chẳng hạn như lớp Human ở trên

public interface IAnimal{
	public default int getId(){
    	System.out.println("IAnimal----->getId");
    	return 0;
    }
}
public interface Runnable{
	public default int getId(){
    	System.out.println("Runnable----->getId");
    }
}
public class Human implements IAnimal, Runnable{
	//.....
}

compiler của java sẽ báo lỗi trong trường hợp này, vì nó không xác định được default method nào sẽ được chọn trong nhiều default method giống khai báo. Trong trường hợp này ta có thể overriden method getId trong class Human để giải quyết.

Overriding method của Object

Nếu chúng ta overriden 1 method của java.lang.Object ví dụ như equals, trình biên dịch sẽ thông báo cho chúng ta 1 error "A default method cannot override a method from java.lang.Object", Java 8 không cho phép override method của Object dưới dạng default method.


All Rights Reserved