CodeGym /Các khóa học /JAVA 25 SELF /Lớp lồng nhau tĩnh (static nested)

Lớp lồng nhau tĩnh (static nested)

JAVA 25 SELF
Mức độ , Bài học
Có sẵn

1. Lớp lồng nhau tĩnh

Lớp lồng nhau tĩnh (static nested class) là lớp được khai báo bên trong một lớp khác với bộ điều chỉnh static. Về bản chất, đây là một lớp bình thường “sống” bên trong lớp khác, nhưng không gắn với đối tượng của lớp bao ngoài.

Nếu lớp inner giống như em trai luôn nắm tay anh trai (đối tượng của lớp bao ngoài), thì lớp lồng nhau tĩnh giống như người anh em họ — chỉ ghé thăm vào dịp gia đình, còn lại sống cuộc đời riêng.

Khác biệt chính:

  • Không có tham chiếu ngầm tới đối tượng của lớp bao ngoài — không hề có OuterClass.this và cũng không truy cập được các thành viên không tĩnh.
  • Có thể chứa các thành viên static (khác với lớp inner thông thường).
  • Được tạo mà không cần đối tượng của lớp bao ngoài.

Cú pháp khai báo

Khai báo lớp lồng nhau tĩnh rất đơn giản: dùng từ khóa static bên trong lớp bao ngoài.

class Outer {
    static class Nested {
        void print() {
            System.out.println("Hello from Nested!");
        }
    }
}

Thế là xong! Không có cú pháp rắc rối — chỉ cần static class.

Minh họa:

Outer (lớp bên ngoài)
│
├── Nested (static nested class)
│      └── print()

2. Tạo instance của lớp lồng nhau tĩnh

Điều thú vị nhất: không cần đối tượng của lớp bao ngoài!

Outer.Nested nested = new Outer.Nested();
nested.print(); // Hello from Nested!

Lưu ý: chúng ta dùng tên đầy đủ của lớp — Outer.Nested. Nó giống như gọi lớp lồng nhau theo “họ”: “Smith.Son”.

So sánh với lớp inner (bên trong):

Outer outer = new Outer();
Outer.Inner inner = outer.new Inner(); // cần đối tượng outer

Còn với static nested — hoàn toàn không cần đối tượng Outer!

3. Truy cập các thành viên của lớp bao ngoài

Đây là điểm khác biệt chính so với lớp inner.

  • Lớp lồng nhau tĩnh chỉ có thể truy cập CÁC THÀNH VIÊN TĨNH của lớp bao ngoài.
  • Không thể truy cập các trường và phương thức không tĩnh (kể cả khi chúng public).

Ví dụ:

class Outer {
    private static int staticValue = 10;
    private int instanceValue = 20;

    static class Nested {
        void show() {
            System.out.println("Static value: " + staticValue); // OK
            // System.out.println("Instance value: " + instanceValue); // Lỗi!
        }
    }
}

Nếu thử truy cập trường không tĩnh instanceValue, trình biên dịch sẽ “nhắc nhở” bạn ngay lập tức.

Tại sao? Vì static nested class không “biết” phải gắn với đối tượng Outer nào — nó không có tham chiếu đến đối tượng của lớp bao ngoài.

4. Khi nào sử dụng lớp lồng nhau tĩnh

Khi nào phù hợp?

  • Khi lớp lồng nhau liên quan về mặt logic với lớp bao ngoài nhưng không cần truy cập đối tượng của lớp bao ngoài.
  • Khi muốn đóng gói cấu trúc hỗ trợ: ví dụ như builder, tiện ích, enum hoặc một đối tượng nhỏ bất biến (immutable).
  • Khi cần giảm “ô nhiễm” package: lớp chỉ cần cho lớp bao ngoài, không cần đưa ra ngoài.

Tình huống điển hình:

  • Mẫu Builder (đặc biệt cho các đối tượng bất biến)
  • Triển khai các cấu trúc hỗ trợ: ví dụ, các lớp Node nội bộ trong collection
  • Nhóm hằng số hoặc tiện ích

Quy tắc chọn nhanh
Hãy tự hỏi: “Lớp lồng nhau của tôi có cần truy cập đối tượng cụ thể của lớp bao ngoài không?”
KHÔNG → dùng static class (lớp lồng nhau tĩnh)
→ dùng class thông thường (lớp inner)

5. Ví dụ sử dụng

Ví dụ 1: Builder cho lớp

Giả sử ta có lớp Person, và muốn hiện thực mẫu Builder cho lớp này:

public class Person {
    private final String name;
    private final int age;

    // Hàm dựng private
    private Person(Builder builder) {
        this.name = builder.name;
        this.age = builder.age;
    }

    // Lớp lồng nhau tĩnh Builder
    public static class Builder {
        private String name;
        private int age;

        public Builder setName(String name) {
            this.name = name;
            return this;
        }

        public Builder setAge(int age) {
            this.age = age;
            return this;
        }

        public Person build() {
            return new Person(this);
        }
    }

    public void printInfo() {
        System.out.println("Person: " + name + ", " + age);
    }
}

Cách dùng:

Person person = new Person.Builder()
    .setName("Ivan")
    .setAge(30)
    .build();

person.printInfo(); // Person: Ivan, 30

Vì sao Builder là lớp lồng nhau tĩnh?
Bởi vì nó không phụ thuộc vào một đối tượng Person cụ thể, mà chỉ giúp tạo ra đối tượng. Nó liên hệ logic với Person, nhưng không gắn với instance cụ thể nào.

Ví dụ 2: Cấu trúc hỗ trợ bên trong collection

Hãy tưởng tượng một lớp “hộp số”, nơi dùng lớp lồng nhau tĩnh Node để lưu trữ phần tử:

public class IntBox {
    private Node head;

    // Lớp static lồng nhau
    private static class Node {
        int value;
        Node next;

        Node(int value) {
            this.value = value;
        }
    }

    public void add(int value) {
        Node node = new Node(value);
        node.next = head;
        head = node;
    }

    public void printAll() {
        Node current = head;
        while (current != null) {
            System.out.println(current.value);
            current = current.next;
        }
    }
}

Cách dùng:

IntBox box = new IntBox();
box.add(1);
box.add(2);
box.add(3);
box.printAll(); // 3 2 1

Vì sao Node là static?
Vì mỗi Node không cần biết về cả “hộp” (IntBox), nó chỉ lưu dữ liệu và liên kết tới Node tiếp theo.

Ví dụ 3: Lớp tiện ích bên trong lớp chính

public class MathUtils {
    // Lớp lồng nhau tĩnh để làm việc với số phức
    public static class Complex {
        private final double re;
        private final double im;

        public Complex(double re, double im) {
            this.re = re;
            this.im = im;
        }

        public Complex add(Complex other) {
            return new Complex(this.re + other.re, this.im + other.im);
        }

        @Override
        public String toString() {
            return re + " + " + im + "i";
        }
    }
}

Cách dùng:

MathUtils.Complex a = new MathUtils.Complex(1, 2);
MathUtils.Complex b = new MathUtils.Complex(3, 4);
MathUtils.Complex sum = a.add(b);
System.out.println(sum); // 4.0 + 6.0i

6. Một số lưu ý hữu ích

Inner class vs lớp lồng nhau tĩnh

Lớp inner (bên trong) Lớp lồng nhau tĩnh (static nested)
Từ khóa không
static
Tham chiếu ngầm đến đối tượng bên ngoài không
Truy cập các thành viên không tĩnh của lớp bên ngoài không
Truy cập các thành viên tĩnh của lớp bên ngoài
Có thể chứa các thành viên static không (chỉ hằng số)
Cú pháp khởi tạo
outer.new Inner()
new Outer.Nested()
Mục đích sử dụng Khi cần truy cập đối tượng của lớp bên ngoài Khi không cần truy cập đối tượng của lớp bên ngoài

Minh họa

flowchart LR
    OuterClass -->|has| InnerClass
    OuterClass -.->|has| StaticNestedClass
    StaticNestedClass -.->|can access| staticMembers
    InnerClass -->|can access| instanceMembers
    InnerClass -->|can access| staticMembers

Đặc điểm và hạn chế

  • Static nested class có thể chứa cả trường và phương thức thông thường lẫn static.
  • Có thể khai báo với bất kỳ phạm vi truy cập nào (public, private, protected, package-private).
  • Có thể triển khai interface và kế thừa từ lớp khác.
  • Có thể là generic.
  • Thường dùng để đóng gói các lớp trợ giúp không cần thiết bên ngoài lớp bao ngoài.

Ví dụ về generic static nested class:

public class Box {
    public static class Holder<T> {
        private T value;
        public Holder(T value) { this.value = value; }
        public T get() { return value; }
    }
}

Khi KHÔNG nên dùng static nested class

  • Nếu lớp lồng nhau cần truy cập các trường/phương thức không tĩnh của lớp bao ngoài — hãy dùng inner class thông thường.
  • Nếu lớp cần dùng bên ngoài lớp bao ngoài — hãy tách ra file riêng.
  • Nếu lớp quá lớn hoặc phức tạp — tốt hơn nên tách thành lớp riêng.

7. Lỗi thường gặp và lưu ý

Lỗi №1: Nhầm lẫn giữa inner và static nested class.
Nhiều người mới cố truy cập các trường không tĩnh của lớp bao ngoài từ static nested class. Nhưng static nested class không có tham chiếu tới đối tượng của lớp bao ngoài, nên điều đó là không thể. Nếu bạn cần truy cập trạng thái của một đối tượng cụ thể — hãy dùng inner class thông thường.

Lỗi №2: Cố tạo static nested class thông qua đối tượng của lớp bao ngoài.
Không cần viết outer.new Inner(). Với static nested class, luôn dùng new Outer.Nested().

Lỗi №3: Dùng static nested class cho logic cần truy cập instance của lớp bao ngoài.
Nếu logic của lớp gắn chặt với trạng thái của đối tượng lớp bao ngoài, static nested class là lựa chọn không phù hợp. Hãy dùng lớp inner thông thường.

Lỗi №4: Lồng nhau quá phức tạp.
Đừng lạm dụng các lớp lồng nhau. Nếu cấu trúc trở nên rối rắm, hãy tách một số lớp ra bên ngoài.

Bình luận
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION