0

Tìm hiểu tính kế thừa trong Java

Tổng quan về tính kế thừa

Trong Java, tính kế thừa là một tính năng cho phép một lớp (class) mới được tạo ra bằng cách sử dụng thông tin và thành phần của một lớp khác. Lớp mới (gọi là lớp con) có thể sử dụng các phương thức và thuộc tính đã được định nghĩa trong lớp hiện có (gọi là lớp cha hoặc lớp cơ sở).

Cú pháp Kế thừa trong Java:

Để kế thừa một lớp trong Java, sử dụng từ khóa extends.

class ParentClass {
    // Các thuộc tính và phương thức của lớp cha
}

class ChildClass extends ParentClass {
    // Các thuộc tính và phương thức của lớp con
}

Đặc điểm của Kế thừa trong Java:

  1. Đơn kế thừa: Trong Java, một lớp chỉ có thể kế thừa từ một lớp khác duy nhất, không hỗ trợ việc kế thừa từ nhiều lớp (multiple inheritance).

  2. Truy cập vào thành phần của lớp cha: Lớp con có thể truy cập các thành phần private của lớp cha thông qua các phương thức public hoặc protected được cung cấp bởi lớp cha.

  3. Ghi đè (Override): Lớp con có thể ghi đè (override) các phương thức của lớp cha bằng cách cung cấp một triển khai mới của phương thức đó trong lớp con. Điều này cho phép lớp con có hành vi khác biệt hoặc tùy chỉnh từ lớp cha.

  4. Tính đa hình (Polymorphism): Kế thừa đi kèm với tính đa hình, cho phép một đối tượng của lớp con có thể được xem như là một đối tượng của lớp cha. Điều này cho phép sử dụng các đối tượng con trong các ngữ cảnh mà yêu cầu các đối tượng cha.

Ví dụ:

class Animal {
    void sound() {
        System.out.println("Some sound");
    }
}

class Dog extends Animal {
    void sound() {
        System.out.println("Bark");
    }
}

class Cat extends Animal {
    void sound() {
        System.out.println("Meow");
    }
}

Trong ví dụ trên, DogCat là hai lớp con kế thừa từ lớp Animal. Mỗi lớp con có một phương thức sound riêng để phản ánh tiếng kêu của chúng, ghi đè phương thức sound từ lớp Animal.

Kế thừa là một khía cạnh quan trọng của lập trình hướng đối tượng trong Java, cho phép tái sử dụng mã và tạo cấu trúc phân cấp trong việc thiết kế lớp.

Keyword "Super"

Trong Java, super là một keyword đặc biệt được sử dụng để tham chiếu đến các thành phần của lớp cha trong lớp con khi có sự kế thừa.

Cách sử dụng super:

  1. Gọi Constructor của lớp cha: Khi tạo một đối tượng của lớp con, super() được sử dụng để gọi constructor của lớp cha. Nó được sử dụng trong phần khai báo constructor của lớp con đầu tiên.

    class ParentClass {
        ParentClass() {
            // Constructor của lớp cha
        }
    }
    
    class ChildClass extends ParentClass {
        ChildClass() {
            super(); // Gọi constructor của lớp cha
            // Các câu lệnh khác của constructor lớp con
        }
    }
    
  2. Truy cập đến phương thức hoặc thuộc tính của lớp cha: super được sử dụng để gọi các phương thức hoặc truy cập các thuộc tính của lớp cha từ lớp con. Điều này cho phép lớp con truy cập các thành phần được định nghĩa trong lớp cha mà không bị ẩn hoặc ghi đè.

    class ParentClass {
        void display() {
            System.out.println("This is from ParentClass");
        }
    }
    
    class ChildClass extends ParentClass {
        void display() {
            super.display(); // Gọi phương thức của lớp cha
            System.out.println("This is from ChildClass");
        }
    }
    

    Trong ví dụ này, super.display() được sử dụng để gọi phương thức display() của lớp cha từ phương thức display() trong lớp con.

  3. Truy cập các constructor khác của lớp cha: Nếu lớp cha có nhiều hơn một constructor, bạn có thể sử dụng super() để chọn constructor cụ thể của lớp cha được gọi từ constructor của lớp con.

    class ParentClass {
        ParentClass(int x) {
            // Constructor có tham số
        }
    
        ParentClass() {
            // Constructor mặc định
        }
    }
    
    class ChildClass extends ParentClass {
        ChildClass() {
            super(5); // Gọi constructor có tham số của lớp cha
            // Các câu lệnh khác của constructor lớp con
        }
    }
    

super giúp quản lý việc truy cập các thành phần của lớp cha từ lớp con một cách dễ dàng và linh hoạt, giữ cho việc kế thừa trở nên linh hoạt và tiện lợi hơn trong Java.

Một số ví dụ

Ví dụ 1:

Một ví dụ về tính kế thừa trong thực tế có thể là hệ thống quản lý nhân viên trong một công ty. Giả sử bạn có các loại nhân viên khác nhau như Nhân viên bán hàng, Nhân viên kỹ thuật và Quản lý. Mỗi loại nhân viên có những tính chất và hành vi riêng, nhưng cũng có những đặc điểm chung.

Lớp cha: Nhân viên (Employee)

class Employee {
    private String name;
    private int employeeId;
    private double salary;

    // Constructors, getters, setters

    public void work() {
        // Công việc chung của mọi nhân viên
    }
}

Lớp con: Nhân viên bán hàng (Salesperson)

class Salesperson extends Employee {
    private int numberOfSales;

    // Constructors, getters, setters

    @Override
    public void work() {
        // Công việc cụ thể của nhân viên bán hàng
        // Cập nhật số lượng bán hàng
    }

    public void makeSalesCall() {
        // Thực hiện cuộc gọi bán hàng
    }
}

Lớp con: Nhân viên kỹ thuật (Technician)

class Technician extends Employee {
    private String techSkill;

    // Constructors, getters, setters

    @Override
    public void work() {
        // Công việc cụ thể của nhân viên kỹ thuật
        // Áp dụng kỹ năng kỹ thuật vào công việc
    }

    public void fixMachine() {
        // Sửa chữa máy móc
    }
}

Lớp con: Quản lý (Manager)

class Manager extends Employee {
    private String department;

    // Constructors, getters, setters

    @Override
    public void work() {
        // Công việc cụ thể của quản lý
        // Quản lý và phân công công việc cho nhân viên
    }

    public void conductMeeting() {
        // Tiến hành cuộc họp
    }
}

Trong ví dụ này, lớp Employee là lớp cha chứa các thuộc tính và phương thức chung của mọi nhân viên. Các lớp con (Salesperson, Technician, Manager) kế thừa từ lớp cha và mở rộng bằng cách thêm các thuộc tính và phương thức riêng biệt cho từng loại nhân viên.

Việc sử dụng tính kế thừa cho phép tái sử dụng mã, giúp quản lý dễ dàng hơn khi mở rộng hệ thống cho các loại nhân viên mới mà không cần viết lại hoặc thay đổi lại mã của lớp cha.

Ví dụ 2:

Hãy tưởng tượng chúng ta xây dựng một hệ thống quản lý đối tượng trong một trò chơi video. Có các loại đối tượng khác nhau như Người chơi (Player), Quái vật (Monster) và Vật phẩm (Item), mỗi loại có những đặc điểm và hành vi riêng. Đồng thời, chúng ta cũng có một lớp cha chung là Đối tượng (GameObject) định nghĩa các thuộc tính và phương thức chung.

Lớp cha: Đối tượng (GameObject)

class GameObject {
    protected int id;
    protected double positionX;
    protected double positionY;

    public GameObject(int id, double x, double y) {
        this.id = id;
        this.positionX = x;
        this.positionY = y;
    }

    public void move(double deltaX, double deltaY) {
        this.positionX += deltaX;
        this.positionY += deltaY;
        System.out.println("Object moved to: (" + positionX + ", " + positionY + ")");
    }
}

Lớp con: Người chơi (Player)

class Player extends GameObject {
    private String playerName;
    private int health;

    public Player(int id, double x, double y, String name) {
        super(id, x, y);
        this.playerName = name;
        this.health = 100;
    }

    public void attack() {
        // Phương thức tấn công của người chơi
    }

    public void useItem(Item item) {
        // Phương thức sử dụng vật phẩm của người chơi
    }
}

Lớp con: Quái vật (Monster)

class Monster extends GameObject {
    private String monsterType;
    private int level;

    public Monster(int id, double x, double y, String type, int lvl) {
        super(id, x, y);
        this.monsterType = type;
        this.level = lvl;
    }

    public void moveRandomly() {
        // Phương thức di chuyển ngẫu nhiên của quái vật
    }

    public void attackPlayer(Player player) {
        // Phương thức tấn công người chơi
    }
}

Lớp con: Vật phẩm (Item)

class Item extends GameObject {
    private String itemName;
    private int quantity;

    public Item(int id, double x, double y, String name, int qty) {
        super(id, x, y);
        this.itemName = name;
        this.quantity = qty;
    }

    public void use() {
        // Phương thức sử dụng vật phẩm
    }

    public void pickUp(Player player) {
        // Phương thức nhặt vật phẩm bởi người chơi
    }
}

Trong ví dụ này, GameObject là lớp cha chứa các thuộc tính và phương thức chung của mọi đối tượng trong trò chơi. Các lớp con (Player, Monster, Item) kế thừa từ GameObject và mở rộng bằng cách thêm các thuộc tính và phương thức đặc biệt cho từng loại đối tượng trong trò chơi.

Việc sử dụng tính kế thừa cho phép chúng ta tận dụng lại mã nguồn, giúp quản lý và mở rộng hệ thống trong trò chơi một cách hiệu quả hơn.

Kết luận

Tính kế thừa là một trong những khía cạnh quan trọng nhất của lập trình hướng đối tượng trong Java và nhiều ngôn ngữ lập trình khác. Nó cung cấp một cách để tái sử dụng mã nguồn, tạo cấu trúc phân cấp linh hoạt và giúp quản lý mã nguồn một cách hiệu quả. Với tính kế thừa, các lớp có thể được xây dựng dựa trên các lớp hiện có, kế thừa các thuộc tính và phương thức từ lớp cha.

Một trong những ưu điểm chính của tính kế thừa là khả năng tái sử dụng mã nguồn. Thay vì viết lại mã cho từng lớp mới, chúng ta có thể kế thừa các tính năng từ lớp cha, giảm thiểu việc lặp lại mã và giúp duy trì mã nguồn dễ dàng hơn. Điều này cũng tạo ra sự linh hoạt trong việc mở rộng và điều chỉnh mã nguồn theo nhu cầu.

Tính kế thừa cũng cung cấp tính đa hình, cho phép một đối tượng của lớp con được coi là một đối tượng của lớp cha. Điều này tạo ra khả năng sử dụng đa dạng và linh hoạt trong việc xử lý các đối tượng theo một cách chung nhất.

Tuy nhiên, việc sử dụng kế thừa cũng đòi hỏi sự cân nhắc và hiểu biết sâu rộng về cấu trúc và quy tắc thiết kế. Một sự kế thừa không đúng đắn có thể dẫn đến các vấn đề như hiện tượng "diamond problem" (vấn đề kim cương) trong trường hợp kế thừa đa cấp và cần phải được quản lý cẩn thận.

Một số nguyên tắc cần nhớ khi triển khai kế thừa bao gồm việc sử dụng super một cách hợp lý, ghi đè phương thức một cách cẩn thận để không làm mất tính đa hình, và hạn chế việc sử dụng kế thừa đa cấp.

Tính kế thừa không chỉ là một công cụ mạnh mẽ để tái sử dụng mã, mà còn là một phần quan trọng trong việc thiết kế và phát triển các hệ thống phức tạp. Sự hiểu biết sâu rộng về tính kế thừa sẽ giúp tối ưu hóa mã nguồn, tạo ra cấu trúc linh hoạt và dễ bảo trì, đồng thời giúp tăng cường khả năng mở rộng của ứng dụng.


All rights reserved

Viblo
Hãy đăng ký một tài khoản Viblo để nhận được nhiều bài viết thú vị hơn.
Đăng kí