Câu hỏi về Interface và abstract trong Java?

Cái này phải làm nhiều thực tế mới “thấm”, chứ giải thích cũng khó hết được. :smiley:
Nhưng tôi xin gợi ý bạn một số thứ có thể tham khảo:

  1. Theo dõi 2 video đầu tiên trong links sau đây để nắm được khái niệm cơ bản của OOP trong Java, trong đó có ví dụ đề cập interface và abstract class: https://www.youtube.com/playlist?list=PLF206E906175C7E07

  2. Bạn muốn so sánh 2 cái này, google “interfaces vs abstract class + java”, có thể tham khảo kết quả: http://www.javatpoint.com/difference-between-abstract-class-and-interface

  3. Theo kinh nghiệm cá nhân của tôi đã có làm một số dự án với Java (mà thực ra tôi chỉ biết mỗi Java thui. ;)). Các khái niệm interfaces và abstract class trong Java, mục đích thực chất là tránh duplicate code (code bị lặp lại) trong lập trình (DRY – Don’t Repeat Yourself rule).

Tùy mục đích sử dụng mà mình có thể chọn interface hay abstract class cho phù hợp. Điểm đáng lưu ý nhất là inteface thì không chứa non-abstract methods (hàm mà có mã thực thi) còn abstract class thì có đấy (xem link ở mục 2. nhé)

Thứ nhất, tôi hay dùng inteface rất nhiều để giải quyết vấn đề duplicated code với Generics. Ví dụ hơi dài, bạn gán theo dõi nhé :smile:

Tôi có 2 class là HelpText và Section, mỗi class đều có thuộc tính riêng của nó, nhưng có chung 1 điểm đều có thuộc tính “name” kiểu String, nhưng lại khác tên biến.

class HelpText{
 private String id;
 private String name;
//getters, setter
}

class Section{
private String id;
private String displayName;
//getters, setters
}

Bài toán đặt ra là viết một hàm để tạo một tên mới cho “name” khi tạo mới 1 object HelpText hay Section. Thông thường bạn sẽ có 2 hàm như vầy:

class GeneratingNameUtils{
    public static String generateUniqueName(Helptext helpText, List<HelpText> helpTexts){
     //implement ở đây và return
     return helpText.name+ "<một nùi gì đó dựa vào cái list helpTexts>";
    }
    
    public static String generateUniqueName(Section section, List<Section> sections){
     //implement ở đây và return
     return section.displayName+ "<một nùi gì đó dựa vào cái list sections>";
    }
}

Rõ ràng là đã xảy ra duplicated code, có sự giống nhau phần implementation chỉ khác nhau class truyền vào giữa 2 hàm trên. Tôi sẽ dùng interface và Generic để giải quyết vấn đề này chỉ với 1 hàm chung.

public interface IUniqueName{
    public String getUniqueName();
}
  
class HelpText implements IUniqueName{
   private String id;
   private String name;
  //getters, setter
  
   public String getUniqueName()  {
     return name;
   } 
 }
      
class Section implements IUniqueName{
  private String id;
  private String displayName;
  //getters, setters
  public String getUniqueName()  {
     return displayName;
  } 
}

class GeneratingNameUtils{
	public static <T> String generateUniqueName(T item, List<T extends IUniqueName> items){
	//implement
 // HelpText hay Section bây giờ đều có  getUniqueName() --> tính đa hình đấy
	return item.getUniqueName() + "<một nùi gì đó dựa vào cái list items>";
	}
}

Có thể code trên bị lỗi syntax, tôi không chắc lắm do lỗi lỗi đánh máy. Cái này là bài toán thực tế trong project của tôi, bạn có thể tham thảo ý tưởng ở đây nhé: http://vhandit.blogspot.com/2015/08/snippet-generate-new-unique-name-string.html. (PR blog :smile:) .

Thứ 2, dùng astract class thì cũng tương tự với vấn đề duplicate code. Bây giờ tôicó 2 class mà trong mỗi class bạn có method y chang nhau luôn. Tôi quyết định dùng một class mới là superclass để chứa các method chung này. Ví dụ

 class A1{
    public void methodA(){
    //code a
    //có gọi methodB();
    }
    
    public void methodB();
    //code b
    }
 }

 class A2{
    public void methodA(){
    //code a
    //có gọi methodB();
    }
    
    public void methodB(){
    //code b1
    }
 }

Bạn thấy rằng cả 2 class A1 và A2 có có phần //code a là y chang chỉ khác mỗi chổ method B() (//code b và //code b1). Bây giờ tôi sẽ dùng abstract class để giải quyết vấn đề này.

class abstract A{
 //non-abstract method
  public void methodA(){
    //code a
    //có gọi methodB();
  }
  
 //abstract method
  public abstract void methodB();

}

class A1 extends A{
    public void methodB();
    //code b
    }
 }

 class A2  extends A{
    public void methodB(){
    //code b1
    }
 }

Okay! Bây giờ A1 và A2 đều có thể sử dụng methodA() mà không cần suy nghĩ đến nước Mỹ, chỉ cần viết phần implement cho methodB() thui, hết duplicated code nhé. :wink:

Thêm nữa, dựa vào interface và abstract class người ta build rất nhiều pattern (Design Pattern) rất hay. Bạn có thể tham khảo pattern đơn giản này để hiểu rõ hơn về abstract class: http://www.tutorialspoint.com/design_pattern/chain_of_responsibility_pattern.htm. Thật ra, tôi thấy mục đích chính của mấy cái này vẫn là DRY. hehe.