Chuyển tới nội dung chính

Lớp Trừu Tượng trong Java 🎨

🎯 Mục tiêu: Học cách sử dụng lớp trừu tượng để tạo các lớp cơ sở và định nghĩa các phương thức chung cho các lớp con trong Java.

Giới thiệu 📝

Lớp trừu tượng (Abstract Class) là một lớp được khai báo với từ khóa abstract và có thể chứa cả phương thức trừu tượng (abstract methods) và phương thức thông thường. Lớp trừu tượng không thể được khởi tạo trực tiếp và phải được kế thừa bởi một lớp con.

💡 Fun Fact: Lớp trừu tượng giúp chúng ta tạo ra các lớp cơ sở chung cho các lớp con, giúp code dễ bảo trì và mở rộng hơn.

1. Cú Pháp Cơ Bản ⚡

Lớp Trừu Tượng Đơn Giản

public abstract class Shape {
protected double area;

// Phương thức trừu tượng
public abstract double calculateArea();

// Phương thức thông thường
public void displayArea() {
System.out.println("Area: " + area);
}
}

// Lớp con
public class Circle extends Shape {
private double radius;

public Circle(double radius) {
this.radius = radius;
}

@Override
public double calculateArea() {
area = Math.PI * radius * radius;
return area;
}
}

Lớp Trừu Tượng với Nhiều Phương Thức

public abstract class Animal {
protected String name;
protected int age;

public Animal(String name, int age) {
this.name = name;
this.age = age;
}

// Phương thức trừu tượng
public abstract void makeSound();
public abstract void move();

// Phương thức thông thường
public void displayInfo() {
System.out.println("Name: " + name + ", Age: " + age);
}
}

2. Các Loại Phương Thức Trong Lớp Trừu Tượng 🔄

Phương Thức Trừu Tượng

public abstract class Vehicle {
// Phương thức trừu tượng không có thân
public abstract void start();
public abstract void stop();
public abstract void accelerate();
}

Phương Thức Thông Thường

public abstract class Database {
protected String connectionString;

// Phương thức thông thường
public void connect() {
System.out.println("Connecting to database...");
}

public void disconnect() {
System.out.println("Disconnecting from database...");
}

// Phương thức trừu tượng
public abstract void query(String sql);
}

Phương Thức Final

public abstract class Payment {
// Phương thức final không thể bị ghi đè
public final void validateAmount(double amount) {
if (amount <= 0) {
throw new IllegalArgumentException("Amount must be positive");
}
}

public abstract void process(double amount);
}

3. Kế Thừa Từ Lớp Trừu Tượng 🧬

Kế Thừa Đơn Giản

public abstract class Shape {
public abstract double calculateArea();
}

public class Rectangle extends Shape {
private double length;
private double width;

public Rectangle(double length, double width) {
this.length = length;
this.width = width;
}

@Override
public double calculateArea() {
return length * width;
}
}

Kế Thừa với Nhiều Phương Thức

public abstract class Employee {
protected String name;
protected double salary;

public Employee(String name, double salary) {
this.name = name;
this.salary = salary;
}

public abstract double calculateBonus();

public void displayInfo() {
System.out.println("Name: " + name);
System.out.println("Salary: " + salary);
System.out.println("Bonus: " + calculateBonus());
}
}

public class Manager extends Employee {
private double bonusRate;

public Manager(String name, double salary, double bonusRate) {
super(name, salary);
this.bonusRate = bonusRate;
}

@Override
public double calculateBonus() {
return salary * bonusRate;
}
}

4. Ví Dụ Thực Tế 🚀

Quản Lý Phương Tiện

public abstract class Vehicle {
protected String brand;
protected String model;
protected int year;
protected double fuelLevel;

public Vehicle(String brand, String model, int year) {
this.brand = brand;
this.model = model;
this.year = year;
this.fuelLevel = 100.0;
}

public abstract void start();
public abstract void stop();
public abstract void refuel();

public void displayInfo() {
System.out.println("Brand: " + brand);
System.out.println("Model: " + model);
System.out.println("Year: " + year);
System.out.println("Fuel Level: " + fuelLevel + "%");
}
}

public class Car extends Vehicle {
private int numberOfDoors;

public Car(String brand, String model, int year, int numberOfDoors) {
super(brand, model, year);
this.numberOfDoors = numberOfDoors;
}

@Override
public void start() {
System.out.println("Starting car...");
}

@Override
public void stop() {
System.out.println("Stopping car...");
}

@Override
public void refuel() {
fuelLevel = 100.0;
System.out.println("Car refueled");
}
}

Quản Lý Hình Học

public abstract class GeometricShape {
protected double area;
protected double perimeter;

public abstract double calculateArea();
public abstract double calculatePerimeter();

public void displayInfo() {
System.out.println("Area: " + calculateArea());
System.out.println("Perimeter: " + calculatePerimeter());
}
}

public class Triangle extends GeometricShape {
private double base;
private double height;
private double side1;
private double side2;

public Triangle(double base, double height, double side1, double side2) {
this.base = base;
this.height = height;
this.side1 = side1;
this.side2 = side2;
}

@Override
public double calculateArea() {
return 0.5 * base * height;
}

@Override
public double calculatePerimeter() {
return base + side1 + side2;
}
}

Quản Lý Phương Thức Thanh Toán

public abstract class PaymentMethod {
protected String cardNumber;
protected String cardHolder;
protected double balance;

public PaymentMethod(String cardNumber, String cardHolder) {
this.cardNumber = cardNumber;
this.cardHolder = cardHolder;
this.balance = 0.0;
}

public abstract void processPayment(double amount);
public abstract void refund(double amount);

public void displayInfo() {
System.out.println("Card Number: " + cardNumber);
System.out.println("Card Holder: " + cardHolder);
System.out.println("Balance: $" + balance);
}
}

public class CreditCard extends PaymentMethod {
private double creditLimit;

public CreditCard(String cardNumber, String cardHolder, double creditLimit) {
super(cardNumber, cardHolder);
this.creditLimit = creditLimit;
}

@Override
public void processPayment(double amount) {
if (amount <= creditLimit) {
balance += amount;
System.out.println("Payment processed: $" + amount);
} else {
System.out.println("Exceeds credit limit");
}
}

@Override
public void refund(double amount) {
balance -= amount;
System.out.println("Refund processed: $" + amount);
}
}

5. Best Practices ✅

  1. Sử Dụng Lớp Trừu Tượng Cho Các Lớp Cơ Sở

    // Không nên
    public class Shape {
    public double calculateArea() {
    return 0;
    }
    }

    // Nên
    public abstract class Shape {
    public abstract double calculateArea();
    }
  2. Định Nghĩa Phương Thức Chung

    // Không nên
    public abstract class Animal {
    public abstract void eat();
    public abstract void sleep();
    public abstract void move();
    }

    // Nên
    public abstract class Animal {
    public abstract void move();
    public void eat() {
    System.out.println("Eating...");
    }
    public void sleep() {
    System.out.println("Sleeping...");
    }
    }
  3. Sử Dụng Phương Thức Final

    // Không nên
    public abstract class Payment {
    public void validateAmount(double amount) {
    if (amount <= 0) {
    throw new IllegalArgumentException();
    }
    }
    }

    // Nên
    public abstract class Payment {
    public final void validateAmount(double amount) {
    if (amount <= 0) {
    throw new IllegalArgumentException();
    }
    }
    }

6. Lỗi Thường Gặp ⚠️

  1. Quên Triển Khai Phương Thức Trừu Tượng

    // Lỗi
    public abstract class Shape {
    public abstract double calculateArea();
    }

    public class Circle extends Shape {
    // Quên triển khai calculateArea()
    }

    // Đúng
    public class Circle extends Shape {
    @Override
    public double calculateArea() {
    return Math.PI * radius * radius;
    }
    }
  2. Khởi Tạo Lớp Trừu Tượng

    // Lỗi
    Shape shape = new Shape(); // Không thể khởi tạo lớp trừu tượng

    // Đúng
    Shape shape = new Circle(5.0); // Khởi tạo lớp con
  3. Quên Gọi Super Constructor

    // Lỗi
    public class Circle extends Shape {
    public Circle(double radius) {
    // Quên gọi super()
    }
    }

    // Đúng
    public class Circle extends Shape {
    public Circle(double radius) {
    super();
    }
    }

7. Ví Dụ Thực Tế Nâng Cao 🎯

Quản Lý Hệ Thống Báo Cáo

public abstract class Report {
protected String title;
protected String author;
protected Date date;
protected List<String> sections;

public Report(String title, String author) {
this.title = title;
this.author = author;
this.date = new Date();
this.sections = new ArrayList<>();
}

public abstract void generateContent();
public abstract void formatReport();

public void addSection(String section) {
sections.add(section);
}

public void displayReport() {
System.out.println("Title: " + title);
System.out.println("Author: " + author);
System.out.println("Date: " + date);
System.out.println("Sections: " + sections);
}
}

public class FinancialReport extends Report {
private double totalRevenue;
private double totalExpenses;

public FinancialReport(String title, String author) {
super(title, author);
this.totalRevenue = 0.0;
this.totalExpenses = 0.0;
}

@Override
public void generateContent() {
addSection("Revenue Analysis");
addSection("Expense Analysis");
addSection("Profit/Loss Statement");
}

@Override
public void formatReport() {
System.out.println("=== Financial Report ===");
displayReport();
System.out.println("Total Revenue: $" + totalRevenue);
System.out.println("Total Expenses: $" + totalExpenses);
System.out.println("Net Profit: $" + (totalRevenue - totalExpenses));
}
}

Quản Lý Hệ Thống Đánh Giá

public abstract class Review {
protected String reviewer;
protected int rating;
protected String comment;
protected Date date;

public Review(String reviewer, int rating, String comment) {
this.reviewer = reviewer;
this.rating = rating;
this.comment = comment;
this.date = new Date();
}

public abstract void validateReview();
public abstract void processReview();

public void displayReview() {
System.out.println("Reviewer: " + reviewer);
System.out.println("Rating: " + rating + "/5");
System.out.println("Comment: " + comment);
System.out.println("Date: " + date);
}
}

public class ProductReview extends Review {
private String productId;
private boolean verifiedPurchase;

public ProductReview(String reviewer, int rating, String comment,
String productId, boolean verifiedPurchase) {
super(reviewer, rating, comment);
this.productId = productId;
this.verifiedPurchase = verifiedPurchase;
}

@Override
public void validateReview() {
if (rating < 1 || rating > 5) {
throw new IllegalArgumentException("Rating must be between 1 and 5");
}
if (comment.length() < 10) {
throw new IllegalArgumentException("Comment must be at least 10 characters");
}
}

@Override
public void processReview() {
validateReview();
displayReview();
if (verifiedPurchase) {
System.out.println("Verified Purchase ✓");
}
}
}

💡 Lời khuyên: Hãy thực hành với các ví dụ thực tế để hiểu rõ hơn về cách sử dụng lớp trừu tượng trong các tình huống khác nhau.

Tiếp Theo 🎯

Trong các bài học tiếp theo, chúng ta sẽ:

  • Tìm hiểu về interfaces
  • Học cách sử dụng multiple inheritance
  • Thực hành với các ví dụ thực tế
  • Tìm hiểu về polymorphism

Lớp trừu tượng trong Java

Lớp trừu tượng (Abstract Class) là một lớp được khai báo với từ khóa abstract và có thể chứa cả phương thức trừu tượng (abstract methods) và phương thức thông thường. Lớp trừu tượng không thể được khởi tạo trực tiếp và phải được kế thừa bởi một lớp con.

1. Cú pháp cơ bản

Lớp trừu tượng đơn giản

public abstract class Shape {
protected double area;

// Phương thức trừu tượng
public abstract double calculateArea();

// Phương thức thông thường
public void displayArea() {
System.out.println("Area: " + area);
}
}

// Lớp con
public class Circle extends Shape {
private double radius;

public Circle(double radius) {
this.radius = radius;
}

@Override
public double calculateArea() {
area = Math.PI * radius * radius;
return area;
}
}

Lớp trừu tượng với nhiều phương thức

public abstract class Animal {
protected String name;
protected int age;

public Animal(String name, int age) {
this.name = name;
this.age = age;
}

// Phương thức trừu tượng
public abstract void makeSound();
public abstract void move();

// Phương thức thông thường
public void displayInfo() {
System.out.println("Name: " + name + ", Age: " + age);
}
}

2. Các loại phương thức trong lớp trừu tượng

Phương thức trừu tượng

public abstract class Vehicle {
// Phương thức trừu tượng không có thân
public abstract void start();
public abstract void stop();
public abstract void accelerate();
}

Phương thức thông thường

public abstract class Database {
protected String connectionString;

// Phương thức thông thường
public void connect() {
System.out.println("Connecting to database...");
}

public void disconnect() {
System.out.println("Disconnecting from database...");
}

// Phương thức trừu tượng
public abstract void query(String sql);
}

Phương thức final

public abstract class Payment {
// Phương thức final không thể bị ghi đè
public final void validateAmount(double amount) {
if (amount <= 0) {
throw new IllegalArgumentException("Amount must be positive");
}
}

public abstract void process(double amount);
}

3. Kế thừa từ lớp trừu tượng

Kế thừa đơn giản

public abstract class Shape {
public abstract double calculateArea();
}

public class Rectangle extends Shape {
private double length;
private double width;

public Rectangle(double length, double width) {
this.length = length;
this.width = width;
}

@Override
public double calculateArea() {
return length * width;
}
}

Kế thừa với nhiều phương thức

public abstract class Employee {
protected String name;
protected double salary;

public Employee(String name, double salary) {
this.name = name;
this.salary = salary;
}

public abstract double calculateBonus();

public void displayInfo() {
System.out.println("Name: " + name);
System.out.println("Salary: " + salary);
System.out.println("Bonus: " + calculateBonus());
}
}

public class Manager extends Employee {
private double bonusRate;

public Manager(String name, double salary, double bonusRate) {
super(name, salary);
this.bonusRate = bonusRate;
}

@Override
public double calculateBonus() {
return salary * bonusRate;
}
}

4. Ví dụ thực tế

Quản lý phương tiện

public abstract class Vehicle {
protected String brand;
protected String model;
protected int year;
protected double fuelLevel;

public Vehicle(String brand, String model, int year) {
this.brand = brand;
this.model = model;
this.year = year;
this.fuelLevel = 100.0;
}

public abstract void start();
public abstract void stop();
public abstract void refuel();

public void displayInfo() {
System.out.println("Brand: " + brand);
System.out.println("Model: " + model);
System.out.println("Year: " + year);
System.out.println("Fuel Level: " + fuelLevel + "%");
}
}

public class Car extends Vehicle {
private int numberOfDoors;

public Car(String brand, String model, int year, int numberOfDoors) {
super(brand, model, year);
this.numberOfDoors = numberOfDoors;
}

@Override
public void start() {
System.out.println("Starting car...");
}

@Override
public void stop() {
System.out.println("Stopping car...");
}

@Override
public void refuel() {
fuelLevel = 100.0;
System.out.println("Car refueled");
}
}

Quản lý hình học

public abstract class GeometricShape {
protected double area;
protected double perimeter;

public abstract double calculateArea();
public abstract double calculatePerimeter();

public void displayInfo() {
System.out.println("Area: " + calculateArea());
System.out.println("Perimeter: " + calculatePerimeter());
}
}

public class Triangle extends GeometricShape {
private double base;
private double height;
private double side1;
private double side2;

public Triangle(double base, double height, double side1, double side2) {
this.base = base;
this.height = height;
this.side1 = side1;
this.side2 = side2;
}

@Override
public double calculateArea() {
return 0.5 * base * height;
}

@Override
public double calculatePerimeter() {
return base + side1 + side2;
}
}

5. Best Practices

  1. Sử dụng lớp trừu tượng cho các lớp có chung hành vi

    // Không nên
    public class Dog { }
    public class Cat { }

    // Nên
    public abstract class Animal {
    public abstract void makeSound();
    }

    public class Dog extends Animal { }
    public class Cat extends Animal { }
  2. Đặt tên lớp trừu tượng rõ ràng

    // Không nên
    public abstract class A { }

    // Nên
    public abstract class PaymentProcessor { }
  3. Tối thiểu hóa số lượng phương thức trừu tượng

    // Không nên
    public abstract class Shape {
    public abstract double getArea();
    public abstract double getPerimeter();
    public abstract double getVolume();
    public abstract void draw();
    public abstract void rotate();
    }

    // Nên
    public abstract class Shape {
    public abstract double getArea();
    public abstract double getPerimeter();

    public void draw() {
    // Implement common drawing logic
    }

    public void rotate() {
    // Implement common rotation logic
    }
    }

6. Lỗi thường gặp

  1. Quên ghi đè phương thức trừu tượng

    // Lỗi
    public abstract class Shape {
    public abstract double calculateArea();
    }

    public class Circle extends Shape {
    // Quên ghi đè calculateArea()
    }

    // Đúng
    public class Circle extends Shape {
    @Override
    public double calculateArea() {
    return Math.PI * radius * radius;
    }
    }
  2. Khởi tạo lớp trừu tượng

    // Lỗi
    public abstract class Animal { }

    Animal animal = new Animal(); // Không thể khởi tạo lớp trừu tượng

    // Đúng
    Animal animal = new Dog(); // Khởi tạo lớp con
  3. Phương thức trừu tượng trong lớp không trừu tượng

    // Lỗi
    public class Shape {
    public abstract double calculateArea(); // Lỗi: phương thức trừu tượng trong lớp thường
    }

    // Đúng
    public abstract class Shape {
    public abstract double calculateArea();
    }