Toàn tập kế thừa (extends) trong Java – https://final-blade.com

Một trong những kỹ năng và kiến thức khá quan trọng nhất trong lập trình Java đó chính là tính thừa kế ( extends ) trong Java. Đây là kỹ năng và kiến thức bạn bắt buộc phải học và hiểu chính sách, ý nghĩa của nó để hoàn toàn có thể vận dụng trong ứng dụng của bạn .

Khi chúng ta nói về tính kế thừa, từ khóa thường xuyên nhất được sử dụng là extends trong java và implements trong java. Tất nhiên sẽ có sự khác biệt giữa 2 từ khóa này. Các bạn có thể xem bài viết phần biệt giữa extends và implements tại đây. Trong bài này tôi sẽ cụ thể hóa và nói kỹ về extends trong Java nhé.

Tại sao sử dụng tính kế thừa trong Java?

Chẳng hạn như Tôi có class Sach gồm 2 thuộc tính là mã và tên của sách, nhưng giờ đây tôi cũng có một class khác là SachLapTrinh, với sách lập trình thì nó cũng có hai thuộc tính là mã và tên của sách, vậy giờ đây yếu tố đặt ra là tôi phải tạo class SachLapTrinh như class Sach gồm 2 thuộc tính trên hay có cách nào để xử lý bài toán này một cách đơn thuần mà không cần tạo 2 thuộc tính trên ? Câu vấn đáp là có. Chúng ta sẽ sử dụng tính thừa kế trong Java .

Để cụ thể và dễ hiểu, hình dùng với ví dụ trên, các bạn xem code bên dưới:

Class Sach:

package pk1;

/**
 *
 * @author PHU TRAN IT
 */
public class Sach {
    private int ma;
    private String name;

   
}

Class SachLapTrinh :

package pk1;

/**
 *
 * @author PHU TRAN IT
 */
public class SachLapTrinh extends Sach{ 
   public static void main(String[] args) {
        SachLapTrinh sLapTrinh = new SachLapTrinh();           
    }
}

Cú pháp của tính kế thừa trong Java

class lopcon extends lopcha{  
   //cac phuong thuc va cac thuoc tinh  
}

Từ khóa extends có ý nghĩa rằng bạn đang tạo một lớp mới và kế thừa từ một lớp đang tồn tại.Trong Java, một lớp mà được kế thừa được gọi là lớp cha, lớp kế kế thừa là lớp con.

Ví dụ tính kế thừa trong Java

Như vậy với ví dụ trên : Sach là lớp cha, SachLapTrinh là lớp con .
Bây giờ tất cả chúng ta cùng nhau test tính thừa kế nhé :

tính kế thừa trong Java

Mặc dù tất cả chúng ta đã kế thừa từ lớp Sach, nhưng SachLapTrinh không có những thuộc tính của lớp cha và như ví dụ trên sẽ bị lỗi. Nhìn kỹ lại code trên, những bạn thấy rằng những thuộc tính của lớp cha pham vi truy vấn là private nên chỉ có phạm phi trong class Sach mới sử dụng được thôi. Cho nên chú ý quan tâm thứ nhất khi thừa kế trong Java là :

Chúng ta sử dụng từ khóa extends của lớp con để có thể kế thừa các thuộc tính của lớp cha trừ các thuộc tính private của lớp cha. 

Tất nhiên các bạn để phạm vi truy cập là public tất nhiên Class SachLapTrinh sẽ kế thừa được những thuộc tính đó. Tuy nhiên vì tính bao đóng trong Java nên khuyên các bạn chúng ta nên khai báo thuộc tính là private. Java cũng khuyến cáo chúng ta như vậy và tất nhiên các lập trình viên cần phải có ý thức để thuộc tính là private.

Tiếp theo tôi sẽ viết một số ít phương pháp ở lớp cha, cho người dùng nhập vào mã và tên sách và phương pháp dùng để in .

Class Sach:
public void nhap(){
        Scanner s = new Scanner(System.in);
        System.out.println("Nhập mã : ");
        this.ma = Integer.parseInt(s.nextLine());
        System.out.println("Nhap tên : ");
        this.name = s.nextLine();
    }

    @Override
    public String toString() {
        return "Sach{" + "ma=" + ma + ", name=" + name + '}';
    }

chạy chương trình ở lớp con ( class SachLapTrinh ) như sau :

public class SachLapTrinh extends Sach{
  
    public static void main(String[] args) {
        SachLapTrinh lapTrinh = new SachLapTrinh();
        lapTrinh.nhap();
        System.out.println(lapTrinh.toString());
           
    }
}

và tất yếu hiệu quả sẽ như mong đợi :

kế thừa trong java

Như vậy, những bạn thấy thừa kế thật tuyệt vời đúng không nào, class SachLapTrinh chả code gì cả nhưng vẫn có những hàm như nhap ( ), toString ( ). Qúa đó, ta thấy được rằng :

Chúng ta sử dụng từ khóa extends của lớp con để có thể kế thừa các phương thức của lớp cha.

Vậy khi làm như trên, có những gì chưa ổn nào ? Chúng ta cùng tâm lý. Giả sử như ở class SachLapTrinh tôi có thêm một thuộc tính nữa là số trang ( sotrang ) của sách .

package pk1;
public class SachLapTrinh extends Sach{
    private int soTrang; 
public static void main(String[] args) {
 SachLapTrinh lapTrinh = new SachLapTrinh();
 lapTrinh.nhap();
 System.out.println(lapTrinh.toString());
 
 }
}

và tất nhiên khi chạy chương trình thì chúng ta không thể nhập được số trang.Vậy chúng ta sẽ làm thế nào? Ta sẽ viết thêm hàm nhập và hàm in cho class SachLapTrinh, đơn giản vì class Sach là lớp cha, no không thể biết là class con cần thêm những phương thức gì, những thuộc tính gì để khai báo.Cách xử lý như sau:

public class SachLapTrinh extends Sach{
    private int soTrang;
    
     public void nhap(){
        super.nhap();
        Scanner s = new Scanner(System.in);
        System.out.println("Nhập số trang : ");
        this.soTrang = Integer.parseInt(s.nextLine());
       
    }
  
    public String toString() {
        return super.toString() + "- số trang : "+soTrang;
    }
    
    public static void main(String[] args) {
        SachLapTrinh lapTrinh = new SachLapTrinh();
        lapTrinh.nhap();
        System.out.println(lapTrinh.toString());
           
    }
}

Ở code trên, các bạn thấy rằng chúng ta viết lại y hệt phương thức của class cha, nhưng thây vì phải nhập lại mã, tên sách, thì tôi sẽ dùng từ khóa super để gọi phương thức của lớp cha. Khi chúng ta viết lại phương thức cùng tên,cùng phạm vi truy cập và cùng kiểu dữ liệu trả về của lớp cha. Vậy khi gọi phương thức nhap() cũng như toString() thì nó sẽ chạy phương thức của lớp con.Và loại bỏ phương thức của thằng cha đi.

Với khái niệm trên chúng ta gọi đây là ghì đè trong java hay còn gọi là override.Nếu Bạn nào chưa tìm hiểu về vấn đề này thì có thể tìm hiểu tại đây. 

Thông thường thì khi sử dụng kế thừa (extends) thì chúng ta sẽ sử dụng ghì đè.

Câu hỏi :   Vì sao phải ghì đè? 

  1. Để class con bảo vệ khá đầy đủ những tính năng để nó hoạt động giải trí tốt .
  2. Để đồng điệu code .

Kết quả chạy chương trình trên :

kế thừa trong java

Vậy theo các bạn phương thức setter và getter trong java có ghì đè được không? Qúa đơn giản, tất nhiên là được vì nó là một hàm bình thường. Tuy nhiên bây giờ theo các bạn, hàm đặc biệt (hàm khởi tạo hay constructor) trong java có ghì đè được không?

Trước khi vấn đáp thắc mắc này, gợi ý những bạn như sau :

Constructor là hàm dùng để tạo ra object từ một class nào đấy. (đương nhiên class nào thì đi kèm với constructor của nó). 

Chắc các bạn có thể dễ dàng đoán ra được rồi với gợi ý của mình rồi chứ? Không nhé.

Nếu class SachLapTrinh ghì đè constructor của lớp Sach thì nó quá không có ý nghĩa và chả có công dụng gì .

Câu hỏi:hàm đặc biệt (hàm khởi tạo hay constructor) trong java có ghì đè được không?

Trả lời :Không, nhưng lại có. (trả lời thế nhà tuyển dụng mới sợ nhé).

Giải thích như sau :
Khi tạo ra object lớp con, thì tất yếu object lớp cha cũng được khởi tạo. Vì sao ? những bạn tâm lý lại xem ở ví dụ trên, nếu không tạo ra object lớp cha thì lớp con sao lấy được mã, tên ?

Nhìn hình minh họa trên những bạn hoàn toàn có thể hiểu. Để tạo object Sach thì gọi constructor của class Sach .
Vậy những bạn chú ý rằng ở lớp con ( class SachLapTrinh ) tất cả chúng ta chưa hề gọi constructor của lớp cha ( Sach ). Thậm chí rằng ở class con tất cả chúng ta chưa hề viết constructor. Nhưng tại sao nó vẫn chạy. Tại vì :

Nguyên tắc :

Trong constructor của class con khi extends phải gọi constructor của lớp cha, nếu không có constructor  thì hệ thống sẽ tự tạo constructor mặc định và gọi constructor của lớp cha.

public SachLapTrinh() {
        super(); // nếu không dùng super thì hệ thống cũng tự gọi.
    }

để chứng tỏ cho yếu tố trên, ở constructor lớp cha, tôi sẽ in ra dòng chữ như sau :

 public Sach() {
        System.out.println("Khoi tao doi tuong lop cha");
    }

Vậy khi chạy :

Vậy như câu trả lời của tôi, không kế thừa nhưng lại kế thừa là ở chỗ đấy. constructor  của lớp con phải bắt buộc gọi constructor  của lớp cha. Nếu không gọi thì mặc định hệ thống sẽ tư gọi.

Kết luận: super cũng dùng để gọi constructor của lớp cha.

Nhưng lại có một trường hợp trơ trêu là constructor của lớp cha không có, hoặc tôi cố ý thay khoanh vùng phạm vi truy vấn thành private thì điều gì xảy ra ? Nó sẽ bị lỗi ngay, vì mặc định mạng lưới hệ thống sẽ gọi constructor của lớp cha, mà constructor lại không có nên đơn nhiên nó bị lỗi. ( Các bạn thử thay vào trong code nhé, tôi lười chụp lại ảnh quá ) .

Vậy chúng ta sẽ xử lý như thế nào? 

  1. Các bạn phải tự gọi constructor của lơp cha, một constructor  nào đấy nhưng là public nhé. Ví dụ constructor  có tham số chẳn hạn.
 public SachLapTrinh() {
        super(1,"tên sách lập trình"); // tự gọi constructor của lớp cha.
   }

2. Thông thường thì tất cả chúng ta không làm như thế này, mà sẽ làm như sau :

public SachLapTrinh(int ma,String ten) {
        super(ma,ten);
    }

làm cách thứ hai thì nhìn vào code rất trong sang và đồng nhất .
Tôi nói thêm như vậy để những bạn hiểu thực chất, chứ thật ra chả ai viết ra constructor mà để private cả ^ ^ .
Chưa hết, giả sử như class của lớp cha, không có constructor nào thì điều gì lại xảy ra tiếp theo ?

Cái này người ta gọi là cấm kế thừa. Một trong những cách cấm kế thừa nữa là ở class các bạn muốn cấm kế thừa thêm vào từ khóa final.

public final class  Sach {}

Vì sao phải cấm kế thừa? Vì một số trường hợp chúng ta đã viết một số class rất hoản chỉnh rồi và cho người khác sử dụng, tất nhiên đã hoàn chỉnh rồi thì thằng nào muốn dùng thì copy class sử dụng, chứ nếu cho nó kế thừa thì uổng công sức bỏ ra viết quá nhỉ. @@.

Lưu ý: Khi kế thừa các bạn phải có ý thức kế thừa chính xác, nghĩa là ví dụ class ConMeo thì không thể extends từ class SinhVat được. Tất nhiên các bạn  code thì nó vẫn chạy nhưng sai về mặt ý nghĩa. Chúng ta là lập trình viên thì phải ý thức được điều đấy.

Đôi lời…

Bài viết trên là những kinh nghiệm tay nghề của tôi, cũng như trong trong thực tiễn những bạn sẽ gặp. Hy vọng những bạn sẽ hiểu toàn bộ những gì được gọi là thừa kế trong java hay sang chảnh thì extends ^ ^ .
Hẹn những bạn ở một bài viết khác .