top of page

JAVA - Ôn lại từ đầu | Tập 1: Đa Hình - Serious Polymorphism

  • Writer: PhongPX
    PhongPX
  • Apr 15, 2020
  • 8 min read

1, Lời nói đầu

Nhân dịp được nghỉ trong thời gian cách ly toàn xã hội, mk có tìm hiểu và ôn tập lại kiến thức Java. Năm 3 mk có được học qua một môn học có tên là "Lập trình Java", cuối kì thì phải nạp lại một project xây dựng chương trình quản lý Coffe, nếu bạn nào tò mò về việc mình xây dựng ứng dụng đó như thế nào thì có thể tham khảo bài viết đó tại đây


Giải thích thêm về cách thức và dự định cho series ôn tập Java này của mình thì các kiến thức mình đưa ra trong mỗi bài viết không phải là do mình tự nghĩ ra mà mình có dựa theo một cuốn sách có tên là "Head First Java". Nội dung về bài viết hôm nay là nói về Đa Hình trong Java là chương 8, do mới nghĩ ra ý tưởng viết blog nên mình chưa có thời gian để note lại kiến thức các chương trước đó đã đọc, do đó, các kiến thức các chương trước đó mình sẽ viết vào một thời gian khác sau khi blog này ra đời. Các bạn có thể hiểu là nội dung của các bài viết này chính là những cảm nhận cá nhân của mình sau khi đọc xong các chương sách



2, Đa hình - Có thể không chỉ như những gì bạn biết

Tiếp theo mình sẽ đưa ra các notes mà khi tìm hiểu về tính đa hình trong JAVA các bạn cần biết, có tất cả 14 Notes tất cả. Lưu ý là nội dung các notes không được trình bày theo tổng hợp kiến thức mà theo các phần giới thiệu trong sách.


NOTE 1: Hiểu đúng ngay từ ban đầu

Để có thể khai thác, tiếp cận một cách đầy đủ về Đa hình thì bắt buộc ta phải nắm vững về Interface.

Interface có thể được hiểu như là một Abstract Class. Nếu còn băn khoăn về khái niệm Abstract Class thì mình có thể nói cho bạn như sau: Một Abstract Class chính là một class mà khi bạn code trong hàm Main thì nó sẽ không được khởi tạo.


NOTE 2: Một số Design và nhận xét

Chúng ta có 3 trường hợp như sau:

 Wolf  aWolf  = new Wolf () ;
Animal aHippo = new Hippo () ; 
Animal  anim = new Animal () ;

Các thiết kế ở ví dụ 1, 2 là hoàn toàn phù hợp và chính xác nhưng ta có vấn đề ở thiết kế thứ 3 bởi vì bản thân Animal là một Abstract class nên không thể được khởi tạo như vậy. Bạn nghĩ sao về Animal Object, nó hoàn toàn không tồn tại.


NOTE 3: Một số class không nên được khởi tạo

Sau đây là 2 trường hợp các class nên và không nên được khởi tạo

Thoải con gà mái: Wolf Object, Hippo Object

Không con chim bông: Animal Object

Câu hỏi:

Làm sao để có thể ngăn chặn các Class đáng nhẽ ra không nên khởi tạo khỏi việc cố tình khởi tạo bởi một số nguyên nhân nào đó ?

Trả lời:

  • Gắn cho nó thêm keyword Abstract, khi đó Compiler sẽ dừng và ngăn chặn bất cứ hành động cố tình khởi tạo các class Abstract ấy.

  • Trong khi thiết kế cấu trúc cây kế thừa {Một chương trình Java có thể hiểu như các Class kế thừa lẫn nhau}, ta phải xác định được đâu là Abstract Class, đâu là Concrete Class {Concrete Class chính là những class cho phép việc khởi tạo Object}

Ví dụ:

abtract class Canime extends Animal {
         public void Roam () ;
}=

NOTE 4: Compiler sẽ làm việc

Một Abstract class sẽ bị Compiler không cho phép việc khởi tạo. Nói như một thuật ngữ phổ biến hiện nay thì Compiler sẽ cách ly Abstract class. Lúc đó Abstract class chỉ có công dụng như là một kiểu Tham chiếu - Reference Type cho các Class khác.

Ví dụ:

abstract public class Canine extends Animals {
           public void roam () {... }
}
public class MakeCanine {
      public void go () {
            Canine c ;    
    //Tuy Canine là Abstract class nhưng ta vẫn có thể khai báo như vậy
            c = new Dog() ; 
    //Ở đây là mới khởi tạo, nhưng không phải là Canime Object

            c = new Canine () ; 
    // Hoàn toàn không được khởi tạo như vậy
            c.roam() ;                
    // Compiler sẽ ngăn chặn việc khởi tạo như vậy
       }
}

Abstract Class hầu như không sử dụng bất cứ một tài nguyên gì(*), không có ý nghĩa hay mục đích gì khi nó không được mở rộng - Extends.

Khi có một Abstract Class thì mọi hành động, phương thức trong nó chỉ có ích khi một class extends từ nó - Instances of Subclass

(*): Có một tài nguyên mà Abstact Class vẫn sử dụng đó là các Static Number


NOTE 5: Abstract Class và Concrete Class

Ghi nhớ một điều sau: Not Abstract = Concrete

Java API cung cấp cho chúng ta rất nhiều Abstract Class đặc biệt là về GUI Component

Một số Abstract GUI Component như Buttons, Text Areas, Scroll Bar, Dialog Boxes...

Khi đó thay vì xây dựng một Component chung (Generic Component ) thì bạn có thế gọi luôn một GUI Component có sẵn ví dụ như JButtton.


NOTE 6: Abstract Methods

Bên cạnh xây dựng các Abstract Class thì ta còn có thể tạo các phương thức ảo - Abstract Methods.

Một Abstract Class thì bắt buộc phải Extends, còn một Abstract Method thì phải Overriden - Ghi đè.

Khi có một Abstract Method thì bên trong nó sẽ không có một đoạn code nào cả.

Ví dụ: public abstract void eat() ;

Điều quan trọng nhất khi bạn muốn tạo một Abstract Method thì bắt buộc chứa nó cũng phải là Abstract Class. Không có chuyện một Abstract Method lại nằm trong một Non-Abstract Class. Tuy nhiên có một điều là nếu ở trong Abstract Class thì ta lại có thể viết cả Abstract Method và Non-Abstract Method


NOTE 7: Giải đáp các thắc mắc

Câu hỏi:

  • Vậy điểm đáng nói khi nhắc tới ý nghĩa thực sự của Abstract Methods là gì ?

  • Nếu như là Abstract Class thì sẽ có code ở trong nó và được kế thừa bới các Class con thì chuyện gì sẽ xảy ra ?

Trả lời:

Trong Abstract Class, Methods sẽ không có ý nghĩa cho đến khi nào các Methods đó được viết lại trong các Class con, khi đó chúng ta mới nhận ra được ý nghĩa thực sự của các Methods đó. Điều quan trọng của Abstract Methods đó chính là dù nếu ở trong nó không có code thì bạn vẫn phải định nghĩa nó lại trong các Class con


NOTE 8: Toàn bộ Abstract Methods cần được định nghĩa lại hết

Khi muốn thực thi một Abstract Method chỉ cần Overriden lại nó là xong, It's so easy! Abstract Method không có Body chương trình, nó tồn tại chỉ đáp ứng tính Đa hình - Polymophism.

Concrete Class đầu tiên kế thừa thì phải thực hiện Overriden tất cả các Abstract Methods.

Ví dụ ta có một chuỗi kế thừa sau : Animal > Canine > Dog

Câu hỏi:

Theo lý thuyết thì khi mà Animal là một Abstract Class chứa Abstract Methods thì Canine phải Overriden tất cả các Abstract Methods đó. Tuy nhiên khi mà Canine cũng là một Abstract Class thì sao ?

Trả lời:

Khi đó mọi trách nhiệm sẽ đổ lên đầu Class Dog ( Huhu, nhưng biết sao bây giờ ). Không chỉ phải Overriden các Abstract Methods của Animal và còn của Canine.

Giải thích một cách chi tiết thì quá trình này có bản chất như sau:

Bạn sẽ phải cung cấp cho Abstract Methods một Body, tức là cùng tên và các đối số được truyền vào, kiểu trả về cũng phải giống với Abstract Method trước đó.


NOTE 9: Thực hành một bài code

Xây dựng một danh sách các chú Dogs

public class MyDogList{
    private Dog[] dogs = new Dog[5];
    private int nextIndex = 0;
    public void add (Dog d){
        if (nextIndex < dogs.length){
            dogs[nextIndex] = d;
            System.out.println("Dog addded at" + nextIndex);
            nextIndex ++;
        }
    }
}

Xây dưng một danh sách các động vật

public class MyAnimalList{
    private Animal[] animals = new Animal[5];
    private int nextIndex = 0;
    public void add (Animal a){
        if (nextIndex < animals.length){
            animals[nextIndex] = a;
            System.out.println("Animal addded at" + nextIndex);
            nextIndex ++;
       }
    }
}

Ghi chú: Đối với một Abstract Class như Animal thì tuy không thể khởi tạo một Animal Object nhưng ta vẫn có thể khởi tạo một Array Object mà có type chính là Animal

private Animal[] animals = new Animal[5];

NOTE 10: Khái niệm Interface

Trong Java thì một Interface có thể được hiểu như 100% Abtract Class. Và không giống như một Abstract Class thì có thể có Abstract Methods hoặc Non - Abstract Methods thì trong Interface hoàn toàn chỉ chứa Abstract Method và cần được Overriden nếu như một Class kế thừa nó.

Định nghĩa một Interface như sau:

public interface Pet {}

Muốn thực thi một Interface thì cú pháp như sau:

public class Dog extends Canine implements Pet {}

Chú ý: Khi bạn Implements một Interface thì bạn vẫn có thể hoàn toàn Extends một Class bình thường như cân đường hộp sữa nha.


NOTE 11: Thực hành code nào

public interface Pet{
    public abstract void beFriendly();
    public abstract void play();
}

public class Dog extends Canine implements Pet{
    public void beFriendly(){...}
    public void play(){...}
    
    public void roam(){...}
    public void eat(){...}
}

NOTE 12: Thực ra chẳng có câu hỏi nào là ngu ngốc cả !

Câu hỏi:

Câu hỏi: Theo tôi nghĩ thì Interfaces thực sự không giúp ích cho việc đa kế thừa bởi vì sẽ không có bất kì một đoạn code thực thi nào sau đó. Đồng thời khi mà tất cả các Methods đều là Abstract thì bản chất của Interface là gì ?

Trả lời:

  • Nên nhớ một điều rằng là Interface thực sự rất là linh họa và đa dụng cho việc đa kế thừa bởi vì bạn có thể sử dụng Interface như một đối số (Arguments) đầu vào và kiểu trả về biết trước, khi đó ta có thể bỏ bất cứ thứ gì vào để chạy (Pass anything).

  • Một Class có thể sẽ không chỉ kế thừa trong một cây kế thừa được định ra trước (Inheritance Tree), nó sẽ vừa có thể kế thừa một Class và đồng thời thực thi một Interface.

  • 2 Class sẽ có thể thực thi cùng một Interface nhưng lại đến từ 2 cây kế thừa khác nhau.

  • Theo định nghĩa thì các Methods trong Interface hoàn toàn là dạng Abstract, tuy nhiên nếu nó có không phải kiểu Abstract thì bắt buộc nó cũng phải bị overriden trong Class thực thi.



NOTE 13: Điều đáng nói ở đây là 1 Class có thể đồng thời thực thi nhiều các Interface một lúc.


public class Dog extends Animal implements Pet, Saveable, Paintable {}

NOTE 14: Sử dụng từ khóa SUPER

Câu hỏi:

Khi tạo một Concrete Class và trong lớp đó bạn cần Overriden một Method, tuy nhiên trong khi Overriden Method đó thì bạn vẫn muốn giữ nguyên Version của Method ấy trong lớp cha thì cần xử lý như thế nào ?

Trả lời:

Sử dụng từ khóa Super


Ví dụ:

abstract class Report{
    void runReport(){
        //Set-up Report    
    }
    void printReport(){
        //generic printing
    }
}

class BuzzWordsReport extends Report{
    void runReport(){
        super.runReport();
        buzzwordCompliance();
        printReport();
        
    }
    void buzzwordCompliance(){...}
}

3, Túm cái bịch Đa Hình

Tính đa hình là khả năng một đối tượng có thể thực hiện một tác vụ theo nhiều cách khác nhau.

Trong Java, chúng ta sử dụng nạp chồng phương thức (method overloading) và ghi đè phương thức (method overriding) để có tính đa hình.

  • Nạp chồng (Overloading): Đây là khả năng cho phép một lớp có nhiều thuộc tính, phương thức cùng tên nhưng với các tham số khác nhau về loại cũng như về số lượng. Khi được gọi, dựa vào tham số truyền vào, phương thức tương ứng sẽ được thực hiện.

  • Ghi đè (Overriding): là hai phương thức cùng tên, cùng tham số, cùng kiểu trả về nhưng thằng con viết lại và dùng theo cách của nó, và xuất hiện ở lớp cha và tiếp tục xuất hiện ở lớp con. Khi dùng override, lúc thực thi, nếu lớp Con không có phương thức riêng, phương thức của lớp Cha sẽ được gọi, ngược lại nếu có, phương thức của lớp Con được gọi.




Comments


Liên lạc tôi bằng cách dưới đây nha !

Thanks for submitting!

bottom of page