Sau khi giới thiệu một số bài viết về Java 8, tôi cũng đã có đề cập đến khái niệm Default Method. Đây là một khái niệm hoàn toàn mới được giới thiệu trong Java 8. Nó mang lại cho người lập trình viên khá nhiều sự hữu ích khi làm việc. Tuy nhiên để sử dụng nó chúng ta cần phải biết được những gì có thể và không thể áp dụng.
Java 8 - Khám phá cách sử dụng Default Method

Default Method là gì?

Java 8 giới thiệu môt khái niệm mới về Default Method dành cho Interface. Nó cho phép chúng ta thêm vào các chức năng cho interface mà không làm phá vỡ các lớp implement từ interface này. Ví dụ, trong Java 8, khi sử dụng ‘List’ hoặc ‘Collection’ interfaces, chúng ta có thể gọi phương thức ‘forEach’ đã được hỗ trợ sẵn để duyệt qua các phần tử.

Chúng ta sẽ bắt đầu với ví dụ minh họa dưới đây
public class MyClass implements InterfaceA {

  public static void main(String[] args) {
    // TODO code application logic here
  }
 
  @Override
  public void saySomething() {
    System.out.println("Hello World");
  }
}
 
interface InterfaceA {
  public void saySomething();
}

Từ đoạn code ở trên các bạn có thể thấy rằng lớp MyClass đang thực thi phương thức saySomeThing() của InterfaceA. Giả sử chúng ta thêm mới một phương thức là sayHello() vào InterfaceA. Khi này, chúng ta sẽ nhận đươc một thông báo lỗi khi phương thức sayHello() chưa được thực thi (Override). Lúc này, các default method trở nên hữu dụng. Chúng ta chỉ cần thêm vào từ khóa default trước access modifier của phương thức và chúng ta không cần phải override lại nó trong lớp MyClass nữa.
public class MyClass implements InterfaceA {
 
  public static void main(String[] args) {
    // TODO code application logic here
  }
 
  @Override
  public void saySomething() {
    System.out.println("This is saySomething() method");
  }
 
}
 
interface InterfaceA {
  public void saySomething();
 
  default public void sayHello() {
    System.out.println("This is sayHello() method");
  }
}
Lưu ý rằng, khi chúng ta sử dụng từ khóa default cho phương thức trong interface, chúng ta bắt buộc phải thêm phần body code cho các phương thức này. Nếu như bạn bỏ qua không thực thi như kiểu default void sayHello(); thì sẽ không biên dịch được nhé.

Điều gì xảy ra khi có đa thừa kế?

Như bạn đã biết thì một class có thể implements nhiều interface cùng 1 lúc. Vậy điều gì sẽ xảy ra khi các interface này có các default methods cùng tên? Conflict

Khi một class thừa kế 2 interface chứa default method cùng tên sẽ xảy ra xung đột. Bởi vì lúc này Java sẽ không biết phải sử dụng phương thức mặc định nào cho phù hợp. Bạn sẽ nhận đươc một thông báo lỗi tương tự như MyClass inherits unrelated defaults for sayHello() from types InterfaceA and InterfaceB.

Ví dụ minh họa
public class MyClass implements InterfaceA, InterfaceB {

  public static void main(String[] args) {
    // TODO code application logic here
  }

  @Override
  public void saySomething() {
    System.out.println("This is saySomething() method");
  }
}
 
interface InterfaceA {

  public void saySomething();

  default public void sayHello() {
    System.out.println("sayHello() from InterfaceA");
  }
}

interface InterfaceB {
  default public void sayHello() {
    System.out.println("sayHello() from InterfaceB");
  }
}

Như đoạn code ở trên chúng ta thấy rằng, nếu trong chúng ta gọi phương thức sayHello() hàm main của lớp MyClass thì chúng ta sẽ bị lỗi ngay lập tức vì Java không biết chính xác chúng ta đang gọi default method của interface nào cả. Để giải quyết vấn đề này chúng ta có 2 cách để giải quyết.

Cách 1: Override lại phương thức sayHello() từ lớp MyClass.

public class MyClass implements InterfaceA, InterfaceB {

  public static void main(String[] args) {
    // TODO code application logic here
  }

  @Override
  public void saySomething() {
    System.out.println("This is saySomething() method");
  }

  @Override
  public void sayHello() {
    System.out.println("implemetation of sayHello() in MyClass");
  }
}
 
interface InterfaceA {

  public void saySomething();

  default public void sayHello() {
    System.out.println("sayHello() from InterfaceA");
  }
}

interface InterfaceB {
  default public void sayHello() {
    System.out.println("sayHello() from InterfaceB");
  }
}

Cách 2: gọi default method của một interface cụ thể bằng cách sử dụng từ khóa super.

public class MyClass implements InterfaceA, InterfaceB {

  public static void main(String[] args) {
    // TODO code application logic here
  }

  @Override
  public void saySomething() {
    System.out.println("This is saySomething() method");
  }

  @Override
  public void sayHello() {
    InterfaceA.super.sayHello(); // Call sayHello() from InterfaceA
  }
}
 
interface InterfaceA {

  public void saySomething();

  default public void sayHello() {
    System.out.println("sayHello() from InterfaceA");
  }
}

interface InterfaceB {
  default public void sayHello() {
    System.out.println("sayHello() from InterfaceB");
  }
}

Nói đến đa thừa kế thì chúng ta cũng biết rằng, một lớp có thể extends từ một superclass và implements các interface khác. Giả sử superclass có phương thức (abstract method hoặc non-abstract method) cùng tên với default method của interface. Thì trong trường hợp này bạn đoán lớp con của chúng sẽ gọi đến phương thức nào?
Java 8 - Đa thừa kế
Kết quả là gì?
a) Hello
b) Hola
c) Compilation error
d) Other

Nếu một lớp con thừa kế một phương thức (abstract hoặc non-abstract) từ một superclass và một phương thức cùng tên trong các super-interface, thì lớp con sẽ thừa kế phương thức của super-class và các phương thức của interface sẽ bị bỏ qua.

Như vậy là tôi đã trình bày cùng các bạn khá chi tiết về Default Method, một chức năng mới, của Java 8 đến các bạn. Các ví dụ khá đơn giản và tôi mong rằng các bạn sẽ hiểu được nó một cách sâu sắc. Đừng ngại ngần để lại comment nếu như bạn có bất kỳ vấn đề nào liên quan đến nội dung bài viết nhé. Chúc các bạn học tốt.
Thương Nguyễn