+1

Nested Classes (P2) Inner Class: Local Classes

Local classes là những class được định nghĩa trong một block, block là 1 group chứa 0 hoặc nhiều câu lệnh giữa các dấu ngoặc. Bạn sẽ thường thấy local classes được định nghĩa bên trong body của một method

Declaring Local Classes

Bạn có thể khai báo một local class bên trong một block bất kì. Ví dụ bạn có thể define ở bên trong một method body, một vòng lặp for, hoặc một mệnh đề if. Xem xét ví dụ bên dưới, LocalClassExample, dùng để validate 2 số điện thoại. Ở đây chúng ta sẽ định nghĩa local class PhoneNumber ở bên trong method validatePhoneNumber:

public class LocalClassExample {
  
    static String regularExpression = "[^0-9]";
  
    public static void validatePhoneNumber(
        String phoneNumber1, String phoneNumber2) {
      
        final int numberLength = 10;
        
        // Valid in JDK 8 and later:
       
        // int numberLength = 10;
       
        class PhoneNumber {
            
            String formattedPhoneNumber = null;

            PhoneNumber(String phoneNumber){
                // numberLength = 7;
                String currentNumber = phoneNumber.replaceAll(
                  regularExpression, "");
                if (currentNumber.length() == numberLength)
                    formattedPhoneNumber = currentNumber;
                else
                    formattedPhoneNumber = null;
            }

            public String getNumber() {
                return formattedPhoneNumber;
            }
            
            // Valid in JDK 8 and later:

//            public void printOriginalNumbers() {
//                System.out.println("Original numbers are " + phoneNumber1 +
//                    " and " + phoneNumber2);
//            }
        }

        PhoneNumber myNumber1 = new PhoneNumber(phoneNumber1);
        PhoneNumber myNumber2 = new PhoneNumber(phoneNumber2);
        
        // Valid in JDK 8 and later:

//        myNumber1.printOriginalNumbers();

        if (myNumber1.getNumber() == null) 
            System.out.println("First number is invalid");
        else
            System.out.println("First number is " + myNumber1.getNumber());
        if (myNumber2.getNumber() == null)
            System.out.println("Second number is invalid");
        else
            System.out.println("Second number is " + myNumber2.getNumber());

    }

    public static void main(String... args) {
        validatePhoneNumber("123-456-7890", "456-7890");
    }
}

Ví dụ này sẽ validate một số điện thoại bằng cách remove tất cả những kí tự ngoại trừ các kí tự từ 0 đến 9. Sau đó, nó sẽ kiểm tra xem số điện thoại đó có chứa chính xác 10 số hay không (độ dài một số điện thoại ở Bắc Mĩ). Ví dụ trên sẽ in ra:

First number is 1234567890
Second number is invalid

Accessing Members of an Enclosing Class

Một local class có thể truy cập đến các member của enclosing class. Ở ví dụ bên trên, constructor của PhoneNumber có thể truy cập đến LocalClassExample.regularExpression. Thêm nữa, một local class có thể access đến các biến local, tuy nhiên, local class chỉ có thể truy cập đến các biến local được khai báo là final. Khi một local class truy cập đến một biến hoặc tham số của enclosing block, nó capture lại biến hay tham số đó. Ví dụ constructor của PhoneNumber có thể truy cập đến biến local numberLength vì nó được khai báo là final, numberLength được gọi là một captured variable Tuy nhiên, bắt đầu từ Java SE 8, local class có thể access tới biến hoặc tham số được khai báo là final hoặc effectively final. Một biến hoặc tham số có giá trị không bao giờ thay đổi sau khi được khởi tạo được gọi là effectively final. Ví dụ, giả sử biến numberLength không được khai báo là final, bỏ comment câu lệnh ở bên trong PhoneNumber constructor, nó sẽ được gán giá trị = 7:

PhoneNumber(String phoneNumber) {
    numberLength = 7;
    String currentNumber = phoneNumber.replaceAll(
        regularExpression, "");
    if (currentNumber.length() == numberLength)
        formattedPhoneNumber = currentNumber;
    else
        formattedPhoneNumber = null;
}

Vì câu lệnh gán giá trị này, biến numberLength đã không còn là effectively final nữa, khi đó Java complier sẽ thông báo một error mesage: "local variables referenced from an inner class must be final or effectively final" ở nơi mà inner class PhoneNumber đang cố gắng truy cập vào biến numberLength:

if (currentNumber.length() == numberLength)

Bắt đầu từ Java SE 8 , nếu bạn khai báo một local class bên trong một method, nó có thể truy cập vào tham số của method đó, ví dụ bạn có thể định nghĩa method sau bên trong PhoneNumber local class

public void printOriginalNumbers() {
    System.out.println("Original numbers are " + phoneNumber1 +
        " and " + phoneNumber2);
}

Method printOriginalNumbers ở đây có thể access vào 2 parameters phoneNumber1 và phoneNumber2 của method validatePhoneNumber.

Shadowing and Local Classes Xem ở part 1: https://viblo.asia/ninh.nguyen.ke/posts/XL6lAY2mlek

Local Classes Are Similar To Inner Classes

Local classes giống với inner classes, không thể định nghĩa hay khai báo bất kỳ static member nào. Local class bên trong một static method, như class PhoneNumber ở trên được định nghĩa trong static method validatePhoneNumber, chỉ có thể tham khảo tới static member của enclosing class. Ví dụ, nếu bạn không khai báo regularExpression là static, Java complier sẽ báo lỗi "non-static variable regularExpression cannot be referenced from a static context." Local classes là non-static vì chúng có quyền truy cập đến các instance members của enclosing block. Kết quả là chúng không thể chứa những hầu hết những loại member static (một số loại được phép sẽ nói ở phần dưới). Bạn không thể khai báo một interface bên trong một block, interface vốn đã là static. Ví dụ, đoạn code dưới không complie được vì interface HelloThere được define bên trong body của method greetInEnglish:

    public void greetInEnglish() {
        interface HelloThere {
           public void greet();
        }
        class EnglishHelloThere implements HelloThere {
            public void greet() {
                System.out.println("Hello " + name);
            }
        }
        HelloThere myGreeting = new EnglishHelloThere();
        myGreeting.greet();
    }

Bạn không thể khai báo một khởi tạo static hay interface trong một local class, đoạn code dưới cũng không thể complie vì method EnglishGoodbye.sayGoodbye được khai báo là static. Java complier sẽ thông báo lỗi kiểu như: "modifier 'static' is only allowed in constant variable declaration":

public void sayGoodbyeInEnglish() {
        class EnglishGoodbye {
            public static void sayGoodbye() {
                System.out.println("Bye bye");
            }
        }
        EnglishGoodbye.sayGoodbye();
    }

Một local class có thể chứa static member nếu chúng là constant variable. Một constant variable là một biến có kiểu nguyên thủy hoặc String, được khai báo final và khởi tạo trong một compile-time constant expression. Một "compile-time constant expression" thường là một string hoặc một biểu thức số học, được tính toán trong complie time. Đoạn code dưới sẽ chạy được vì static member EnglishGoodbye.farewell là một constant variable:

    public void sayGoodbyeInEnglish() {
        class EnglishGoodbye {
            public static final String farewell = "Bye bye";
            public void sayGoodbye() {
                System.out.println(farewell);
            }
        }
        EnglishGoodbye myEnglishGoodbye = new EnglishGoodbye();
        myEnglishGoodbye.sayGoodbye();
    }

Nguồn bài viết

https://docs.oracle.com/javase/tutorial/java


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í