[Tự học lập trình Java] Các lớp lồng trong Java

 

Lập trình hướng đối tượng với Java - tinhoccoban.net

Các lớp lồng.

 Giới thiệu

Ngôn ngữ lập trình Java cho phép bạn định nghĩa một lớp trong một lớp khác. Một lớp như vậy được gọi là một lớp lồng và được minh họa như sau:

class OuterClass {
    ...
    class NestedClass {
        ...
    }
}

Thuật ngữ: Các lớp lồng được chia thành hai thể loại: non-staticstatic. Lớp lồng non-static được gọi là lớp trong. Lớp lồng được khai báo static được gọi là lớp lồng tĩnh.

class OuterClass {
    ...
    class InnerClass {
        ...
    }
    static class StaticNestedClass {
        ...
    }
}

Một lớp lồng là một thành viên của lớp bao quanh nó. Lớp lồng non-static (lớp trong) có được truy cập các thành viên khác trong lớp bao quanh nó, nếu chúng được khai báo là private. Lớp lồng tĩnh không truy cập được các thành viên khác trong lớp bao quanh nó. Như một thành viên của lớp OuterClass, một lớp lồng có thể được định nghĩa private, public, protected, hoặc packate-private. (Gọi lại lớp ngoài có thể chỉ được định nghĩa public hoặc package-private).

Tại sao sử dụng các lớp lồng?

Các lý do thuyết phục để sử dụng các lớp lồng bao gồm:

·        Nó là một cách cục bộ các nhóm lớp chỉ được sử dụng trong một nơi: Nếu một lớp là được sử dụng chỉ với một lớp khác, thì là hợp lý để nhúng nó vào lớp đó và giữ chúng bên nhau. Lớp lồng như “các lớp hỗ trợ” tạo cho các gói của chúng sắp xếp hợp lý hơn.

·        Tăng tính đóng gói: Xem xét hai mức đột các lớp, A và B, nơi B cần truy cập các thành viên của lớp A mà hơn nữa chúng được khai báo private. Bằng cách ẩn lớp B trong lớp A, các thành viên cùa lớp A có thể được khai báo private và B có thể truy cập được chúng. Ngoài ra, bản thân B có thể được ẩn đi từ bên ngoài.

·        Nó có thể dẫn đến dễ đọc và dễ bảo trì mã nguồn hơn: Việc đặt lớp lồng trong lớp ở mức cao nhất sẽ đặt mã gần nhất với nơi nó được sử dụng.

Các lớp trong.

Giống như instance methods và các biến, một lớp trong được liên kết với một cá thể của lớp bao quanh nó và truy cập trực tiếp vào các phương thức và thuộc tính của đối tượng. Ngoài ra, bởi vì một lớp bên trong được liên kết với một cá thể, nó không thể định nghĩa bất kỳ một thành viên tĩnh nào bên trong nó.

Các đối tượng là cá thể của một lớp trong tồn tại trong một cá thể của lớp ngoài. Xem xét lớp sau:

class OuterClass {
    ...
    class InnerClass {
        ...
    }
}

Một cá thể của lớp InnerClass có thể chỉ tồn tại trong một cá thể OuterClass và có thể truy cập trực tiếp đến các phương thức và các thuộc tính của cá thể lớp ngoài.

Để khởi tạo một lớp trong, bạn trước hết phải khởi tạo lớp bên ngoài. Sau đó, tạo một đối tượng lớp trong bên trong lớp ngoài với cú pháp này:

OuterClass outerObject = new OuterClass();
OuterClass.InnerClass innerObject = outerObject.new InnerClass();

Có hai loại đặc biệt của các lớp trong: lớp cục bộ và lớp vô danh sẽ được bàn đến ở phần sau.

Các lớp lồng tĩnh.

Như  các phương thức class methods và các biến, một lớp lồng tĩnh được liên kết với các lớp bên ngoài. Và giống như phương thức tĩnh (static class methods), một lớp lồng tĩnh không thể tham chiếu trực tiếp đến các biến instance variable hoặc các phương thức định nghĩa bên trong lớp bao bọc nó: nó có thể sử dụng chúng chỉ trong một đối tượng tham chiếu. Trong phần ví dụ lớp trong và lớp lồng sẽ minh họa vấn đề này.

Chú ý: một lớp lồng tĩnh tương tác với các thành viên instance của lớp ngoài (và các lớp khác) chỉ như bất kỳ lớp khác ở mức cao nhất. Minh chứng là, một lớp lồng tĩnh về mặt hành vi là của một lớp mức cao nhất để thuận lợi cho việc đóng gói. Trong ví dụ về lớp trong và lớp lồng tĩnh cũng mình họa điều này.

Bạn khởi tạo một lớp lồng tĩnh như cách khởi tạo của một lớp mức cao nhất:

StaticNestedClass staticNestedObject = new StaticNestedClass();

Ví dụ về các lớp trong và các lớp lồng tĩnh.

Trong ví dụ, OuterClass, cùng với lớp TopLevelClass minh họa các thành viên lớp của OuterClass một lớp trong (InnerClass), một lớp lồng tĩnh (StaticNestedClass), và một lớp mức cao nhất (TopLevelClass) cần truy cập:

Kết quả của chương trình này là:

Inner class:
------------
Outer field
Static outer field
 
Static nested class:
--------------------
Outer field
Static outer field
 
Top-level class:
--------------------
Outer field
Static outer field

Chú ý rằng một lớp lồng tĩnh tương tác với các thành viên instance của lớp ngoài chỉ như bất kỳ lớp mức độ cao nhất khác. Trong lớp lồng tĩnh StaticNestedClass không thể truy cập trực tiếp vào thuộc tính outerField bởi vì nó là một biến instance của lớp bao bọc nó, OuterClass.  Trình biên dịch sẽ tạo ra lỗi tại câu lệnh được in đậm dưới đây:

static class StaticNestedClass {
    void accessMembers(OuterClass outer) {
       // Compiler error: Cannot make a static reference to the non-static
       //     field outerField
       System.out.println(outerField);
    }
}

Để sửa lỗi này, truy cập vào outerField thông qua một đối tượng tham chiếu:

System.out.println(outer.outerField);

 Tương tự, lớp mức độ cao nhất TopLevelClass cũng không thể truy cập vào trường outerField.

Làm mờ (Shadowing)

Nếu một khai báo cho một kiểu (như là một biến thành viên hoặc một tên tham số) trong một phạm vi cụ thể (như trong một lớp trong hoặc một khai báo phương thức) có tên giống với khai báo của phạm vi ngoài, thì khai báo làm mờ shadows phạm vi xunh quanh. Bạn không thể tham chiếu đến một khai báo làm mờ bằng tên độc lập. Trong ví dụ ShadowTest, minh họa điều này:

public class ShadowTest {
 
    public int x = 0;
 
    class FirstLevel {
 
        public int x = 1;
 
        void methodInFirstLevel(int x) {
            System.out.println("x = " + x);
            System.out.println("this.x = " + this.x);
            System.out.println("ShadowTest.this.x = " + ShadowTest.this.x);
        }
    }
 
    public static void main(String... args) {
        ShadowTest st = new ShadowTest();
        ShadowTest.FirstLevel fl = st.new FirstLevel();
        fl.methodInFirstLevel(23);
    }
}

Kết quả của chương trình này:

x = 23
this.x = 1
ShadowTest.this.x = 0

Ví dụ này định nghĩa ba biến tên x: biến thành viên của lớp ShadowTest, biến thành viên của lớp trong FirstLevel, và tham số trong phương thức methodInFirstLevel. Biến x được định nghĩa là một tham số của phương thức methodInFirstLevel làm mở đi biến trong lớp FirstLevel. Hệ quả là khi bạn sử dụng biến x trong phương thức methodInFirstLevel, nó tham chiếu đến tham số phương thức. Để tham chiếu đến biến thành viên của lớp FirstLevel, sử dụng từ khóa this để đại diện cho phạm vi bao quanh:

System.out.println("this.x = " + this.x);

Tham chiếu đến biến thành viên của phạm vi bao quanh lớn hơn bằng tên lớp mà chúng thuộc về. Ví dụ, câu lệnh truy cập biến thành viên của lớp ShadowTest từ phương thức methodInFirstLevel:

System.out.println("ShadowTest.this.x = " + ShadowTest.this.x);

Tuần tự hóa

Tuần tự hóa của các lớp trong, bao gồm các lớp địa phương và các lớp vô danh, rất không được khuyến khích. Khi chương trình Java biên dịch cấu trúc nhất định, như các lớp trong, nó tạo ra cấu trúc tổng hợp; có các lớp, các phương thức, các thuộc tính, và các cấu trúc khác không có cấu trúc tương ứng trong mã nguồn. Các cấu trúc tổng hợp cho phép Java biên dịch thực hiện các tính năng ngôn ngữ không có sự thay đổi JVM. Tuy nhiên, các cấu trúc tổng hợp có thể thay đổi khác nhau giữa các trình biên dịch thực hiện, có nghĩa là file .class có thể thay đổi giữa các cách triển khai khác nhau. Hệ quả là bạn có thể giải quyết những vấn đề tương thích nếu bạn tuần tự hóa một lớp trong và sau đó giải mã hóa nó bằng một triển khai JRE khác. Xem trong phần các tham số ngầm và tổng hợp cho nhiều thông tin hơn về các cấu trúc ngầm được tạo ra khi một lớp trong được biên dịch.

Ví dụ về lớp trong

Để xem một lớp trong được sử dụng, trước hết xem xét một mảng. Trong ví dụ, bạn tạo một mảng, điền các giá trị số nguyên, và sau đó đầu ra chỉ các giá trị có chỉ số chẵn tăng dần của mảng.

public class DataStructure {
    
    // Create an array
    private final static int SIZE = 15;
    private int[] arrayOfInts = new int[SIZE];
    
    public DataStructure() {
        // fill the array with ascending integer values
        for (int i = 0; i < SIZE; i++) {
            arrayOfInts[i] = i;
        }
    }
    
    public void printEven() {
        
        // Print out values of even indices of the array
        DataStructureIterator iterator = this.new EvenIterator();
        while (iterator.hasNext()) {
            System.out.print(iterator.next() + " ");
        }
        System.out.println();
    }
    
    interface DataStructureIterator extends java.util.Iterator<Integer> { } 
 
    // Inner class implements the DataStructureIterator interface,
    // which extends the Iterator<Integer> interface
    
    private class EvenIterator implements DataStructureIterator {
        
        // Start stepping through the array from the beginning
        private int nextIndex = 0;
        
        public boolean hasNext() {
            
            // Check if the current element is the last in the array
            return (nextIndex <= SIZE - 1);
        }        
        
        public Integer next() {
            
            // Record a value of an even index of the array
            Integer retValue = Integer.valueOf(arrayOfInts[nextIndex]);
            
            // Get the next even element
            nextIndex += 2;
            return retValue;
        }
    }
    
    public static void main(String s[]) {
        
        // Fill the array with integer values and print out only
        // values of even indices
        DataStructure ds = new DataStructure();
        ds.printEven();
    }
}

Đầu ra của chương trình là:

0 2 4 6 8 10 12 14

Chú ý rằng lớp EvenIterator tham chiếu trực tiếp đến arrayOfInts là biến instance của đối tượng DataStructure.

Bạn có thể sử dụng các lớp trong để thực hiện các lớp tiện ích như một thể hiện trong ví dụ này. Để xử lý các dữ kiện của giao diện người dùng, bạn phải biết sử dụng các lớp trong, bởi vì cơ chế xử lý các sự kiện sử dụng rộng rãi.

Các lớp cục bộ và lớp vô danh.

Có hai kiểu được thêm vào cho các lớp trong. Bạn có thể khai báo một lớp trong bên trong phần thân của một phương thức. Các lớp như vậy được biết đến là các lớp cục bộ. Bạn có thể cũng khai báo một lớp một lớp trong bên trong một phương thức nhưng không khai báo tên. Các lớp như vậy được biết đến là các lớp vô danh.

Các bổ ngữ.

Bạn có thể sử dụng các bổ ngữ giống nhau cho các lớp trong như bạn sử dụng cho các thành viên khác trong lớp ngoài. Ví dụ, bạn có thể sử dụng từ chỉ định truy cập như private, public, protected để hạn chế truy cập đến các lớp trong, giống như bạn sử dụng chúng để hạn chế truy cập cho các thành viên khác của lớp.

Các lớp cục bộ

Giới thiệu.

Các lớp cục bộ là các lớp được khai báo trong một khối, bao gồm nhóm lệnh trong cặp mở/ đóng ngoặc nhọn. Bạn thường tìm thấy các lớp cục bộ được định nghĩa trong một phương thức.

Phần này bao gồm các chủ đề sau:

·        Khai báo các lớp cục bộ.

·        Truy cập các thành viên trong lớp bao quanh.

o   Làm mờ và các lớp cục bộ.

·        Các lớp cục bộ tương tự các lớp trong.

Khai báo các lớp cục bộ.

Bạn cần định nghĩa một lớp cục bộ bên trong một khối (Xem phần biểu thức, các câu lệnh và các khối để thêm thông tin). Ví dụ, bạn có thể định nghĩa một lớp cục bộ trong thân một phương thức, một vòng lặp for hoặc một câu lệnh điều kiện if.

Ví dụ LocalClassExample, kiểm tra hai số điện thoại. Định nghĩa một lớp cục bộ PhoneNumber trong phương thức 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");
    }
}

Trong ví dụ kiểm tra một số điện thoại bằng cách lấy ra số đầu tiên của tất cả các ký tự từ số điện thoại ngoại trừ các số từ 0 đến 9. Sau đó, nó kiểm tra các số điện thoại bao gồm chính xác 10 số (kích thước của một số điện thoại ở Bắc Mỹ). Ví dụ này in ra:

First number is 1234567890
Second number is invalid

Truy cập các thành viên của một lớp bao bọc bên ngoài lớp cục bộ.

Một lớp cục bộ có thể truy cập vào các thành viên của lớp bao bọc bên ngoài. Trong ví dụ trước, hàm khởi tạo PhoneNumber truy cập vào thành viên LocalClassExample.regularExpression.

Ngoài ra, một lớp cục bộ có thể truy cập vào các biến cục bộ. Tuy nhiên, một lớp cục bộ chỉ có thể truy cập vào các biến cục bộ được định nghĩa là final. Khi một lớp cục bộ truy cập vào một biến cục bộ hoặc tham số của khối đóng mở ({}), nó nắm bắt được biến hoặc tham số. Ví dụ hàm khởi tạo PhoneNumber có thể truy cập vào biến cục bộ numberLength bởi vì biến này được khai báo là final; numberLangth là một biến nắm bắt được của lớp cục bộ, gọi là biến captured variable.

Tuy nhiên, bắt đầu từ Java SE 8, một lớp cục bộ có thể truy cập các biến cục bộ và các tham số trong khối đóng mở ({}) được khai báo final hoặc effectively final. Một biến hoặc tham số có giá trị không bao giờ thay đổi sau khi khởi tạo là effectively final. Ví dụ, giả sử biến numberLength không được khai báo là final, và trong lệnh gán được bôi đậm trong hàm khởi tạo PhoneNumber dưới đây thay đổi độ dài của một số điện thoại đến 7 số:

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

Bởi vì ở câu lệnh gán, biến numberLength không là effectively final. Có một kết quả, trình biên dịch Java tạo ra một lỗi tương tự như ("local variables referenced from an inner class must be final or effectively final") tạm dịch là các biến tham chiếu cục bộ từ một lớp trong phải được khai báo là final hoặc effectively final, khi một lớp trong PhoneNumber có gắng truy cập vào biến numberLenth:

if (currentNumber.length() == numberLength)

Bắt đầu từ Java SE 8 trở đi, nếu bạn khai báo một lớp trong trong một phương thức, nó có thể truy cập được các tham số của phương thức. Ví dụ, bạn có thể khai báo phương thức sau trong lớp cục bộ PhoneNumber:

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

Phương thức printOriginalNumbers truy cập các tham số phoneNumber1phoneNumber2 của phương thức validateNumber.

Làm mờ và các biến cục bộ: Khai báo một kiểu (như là một biến) trong một lớp cục bộ làm mờ khai báo trong phạm vi bao bọc nó thống như cái cái tên “làm mờ”. Xem lại phần làm mờ (Shadowing trong để thêm thông tin)

Các lớp cục bộ tương đương với các lớp trong.

Các lớp cục bộ là tương đương với các lớp trong bởi chúng không thể định nghĩa hoặc khai báo bất kỳ một thành viên tĩnh nào. Lớp cục bộ trong phương thức tĩnh, giống như lớp PhoneNumber nó định nghĩa trong phương thức validatePhoneNumber, chỉ có thể tham chiếu đến các thành viên tĩnh trong khối đóng mở của lớp. Ví dụ, nếu bạn không định nghĩa một biến thành viên regularExpression như một biến tĩnh, thì trình biên dịc Java sẽ tạo ra một lỗi là “non-static variable regularExpression cannot be referenced from a static context”, tạm dịch là biến regularaExpression không thể là biến tĩnh, nó không thể tham chiếu đến từ một ngữ cảnh tĩnh (hiểu là là biến tĩnh).

Các lớp cục bộ không thể là lớp tĩnh bởi vì chúng truy cập vào các thành viên instance bên trong khối. Hệ quả là chúng không thể bao gồm hầu hết các loại khai báo tĩnh.

Bạn không thể khai báo một interface trong một khối; các interface vốn dĩ là tĩnh. Ví dụ, đoạn trích mã nguồn dưới đây không thể biên dịch được bởi interface HelloThere được khai báo bên trong thân của phương thức 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 các khởi tạo tĩnh hoặc các interface thành viên trong một lớp cục bộ. Đoạn trích mã nguồn dưới đây không thể biên dịch bởi phương thức EnglishGoodbye.sayGoodbye được khai báo là static. Trình biên dịch sinh ra lỗi như “modifier 'static' is only allowed in constant variable declaration”, tạm dịch là bổ ngữ ‘static’ chỉ cho phép khai báo biến hằng. Nó được định nghĩa như này:

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

Một lớp cục bộ có thể có các thành viên tĩnh, chúng là các biến hằng (Một biến hằng là một biến có kiểu nguyên thủy hoặc kiểu String, được khai báo là final và khởi tạo với một biểu thức hằng thời gian biên dịch. Một biểu thức hằng thời gian biên dịch thường là một chuỗi hoặc một biểu thức số nó có thể được đánh giá bằng thời gian biên dịch. Xem lại phần hiểu về các thành viên của lớp trong phần trước để rõ hơn thông tin.) Ví dụ đoạn trích mã nguồn biên dịch bởi thành biên tĩnh EnglishGoodbye.farewell là một biến hằng:

    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();
    }

 

Các lớp vô danh (Anonymous Classes)

Các lớp vô danh cho phép bạn tạo ra mã nguồn ngắn gọn. Chúng cho phép bạn khai báo và khởi tạo một lớp trong cùng thời gian. Chúng giống như lớp cục bộ ngoại trừ chúng không có tên. Sử dụng chúng nếu bạn cần sử dụng một lớp cục bộ một lần.

Trong phần này sẽ có các chủ đề sau:

·        Khai báo các lớp vô danh.

·        Cú pháp trong các lớp vô danh.

·        Truy cập các biến cục bộ của khối, và khai báo và truy cập các thành viên trong các lớp vô danh.

·        Các ví dụ về các lớp vô danh.

Khai báo các lớp  vô danh.

Trong khi các lớp cục bộ là các lớp khai báo, thì lớp vô danh là một biểu thức, có nghĩa bạn định nghĩa một lớp trong một biểu thức khác. Ví dụ HelloWorldAnonymousClasses sử dụng các lớp vô danh trong câu lệnh khai báo của các biến cục bộ frenchGreeting và spanishGreeting, nhưng sử dụng trong một lớp cục bộ cho khai báo các biến englishGreeting:

public class HelloWorldAnonymousClasses {
  
    interface HelloWorld {
        public void greet();
        public void greetSomeone(String someone);
    }
  
    public void sayHello() {
        
        class EnglishGreeting implements HelloWorld {
            String name = "world";
            public void greet() {
                greetSomeone("world");
            }
            public void greetSomeone(String someone) {
                name = someone;
                System.out.println("Hello " + name);
            }
        }
      
        HelloWorld englishGreeting = new EnglishGreeting();
        
        HelloWorld frenchGreeting = new HelloWorld() {
            String name = "tout le monde";
            public void greet() {
                greetSomeone("tout le monde");
            }
            public void greetSomeone(String someone) {
                name = someone;
                System.out.println("Salut " + name);
            }
        };
        
        HelloWorld spanishGreeting = new HelloWorld() {
            String name = "mundo";
            public void greet() {
                greetSomeone("mundo");
            }
            public void greetSomeone(String someone) {
                name = someone;
                System.out.println("Hola, " + name);
            }
        };
        englishGreeting.greet();
        frenchGreeting.greetSomeone("Fred");
        spanishGreeting.greet();
    }
 
    public static void main(String... args) {
        HelloWorldAnonymousClasses myApp =
            new HelloWorldAnonymousClasses();
        myApp.sayHello();
    }            
}

Cú pháp của các lớp vô danh.

Được nhắc đến trong phần trước, một lớp vô danh là một biểu thức. Cú pháp của một biểu thức lớp vô danh là giống lời gọi một hàm khởi dựng, ngoại trừ việc có một định nghĩa nội dung bên trong một khối mã nguồn.

Xem xét khởi tạo đối tượng frenchGreeting:

        HelloWorld frenchGreeting = new HelloWorld() {
            String name = "tout le monde";
            public void greet() {
                greetSomeone("tout le monde");
            }
            public void greetSomeone(String someone) {
                name = someone;
                System.out.println("Salut " + name);
            }
        };

Biểu thức lớp vô danh bao gồm:

·        Toán tử new

·        Tên của một interface để thực hiện hoặc một lớp kế thừa. Trong ví dụ này, lớp vô danh thực hiện interface HelloWorld.

·        Dấu ngoặc đơn bao gồm các đối số ở trong hàm khởi tạo, chỉ giống một instance được tạo ra từ biểu thức tạo lớp. Chú ý: khi bạn thực hiện một interface, nó không có hàm khởi tạo, do vậy bạn sử dụng một cặp đóng mở ngoặc đơn bên trong để trống, như ví dụ này.

·        Một thân, một lớp được khai báo là một thân. Hơn nữa ,trong thân lớp, các phương thức được phép, còn các câu lệnh thì không được phép khai báo.

Bởi vì một lớp vô danh định nghĩa là một biểu thức, nó phải có thành phần của một câu lệnh. Trong ví dụ, biểu thức lớp vô danh là một phần của câu lệnh đó là một cá thể đối tượng frenchGreeting. (Điều này giải thích tại sao có dấu chấm phẩy sau dấu ngoặc.)

Truy cập các biến cục bộ trong phạm vi bao bọc, và khai báo và truy cập các thành viên của lớp vô danh.

Giống như các lớp cục bộ, lớp vô danh có thể nắm bắt các biến; chúng có thể truy cập vào cùng các biến cục bộ của phạm vi bao bọc nó.

·        Một lớp ẩn danh có thể truy cập vào các thành viên của lớp bao bọc nó.

·        Một lớp ẩn danh không thể truy cập vào các biến trong phạm vi bao bọc nó mà không được khai báo final hoặc effectively final.

·        Giống như một lớp lồng, một khai báo cho kiểu (giống như một biến) trong một lớp ẩn danh làm mờ bất kỳ một khai báo khác trong phạm vi bao bọc nếu nó cùng tên. Xem lại làm mờ ở phần trên để biết thêm thông tin.

Các lớp ẩn danh cũng có cùng hạn chế giống các lớp cục bộ cho các mối quan hệ cho các thành viên của chúng:

·        Bạn không thể khai báo khởi tạo tĩnh hoặc thành viên interface trong một lớp ẩn danh.

·        Một lớp ẩn danh có thể có các thành viên tĩnh nhưng chúng phải là các biến hằng.

Lưu ý rằng bạn có thể khai báo những điều sau cho lớp ẩn danh:

·        Fields (các thuộc tính).

·        Các phương thức bổ sung (ngay cả khi chúng không triển khai bất kỳ một phương thức nào cho supertype).

·        Khởi tạo cho cá thể.

·        Các lớp cục bộ.

·        Tuy nhiên, bạn không thể khai báo các hàm khởi tạo trong một lớp vô danh.

Ví dụ vê lớp Vô danh.

Các lớp vô danh thường sử dụng trong một ứng dụng giao diện đồ họa (GUI).

Xem xét trong JavaFX ví dụ Helloworld.java. Trong ví dụ này, bao gồm nút Say ‘Hello World’. Lớp vô danh trong phần highlight:

import javafx.event.ActionEvent;
import javafx.event.EventHandler;
import javafx.scene.Scene;
import javafx.scene.control.Button;
import javafx.scene.layout.StackPane;
import javafx.stage.Stage;
 
public class HelloWorld extends Application {
    public static void main(String[] args) {
        launch(args);
    }
    
    @Override
    public void start(Stage primaryStage) {
        primaryStage.setTitle("Hello World!");
        Button btn = new Button();
        btn.setText("Say 'Hello World'");
        btn.setOnAction(new EventHandler<ActionEvent>() { 
            @Override
            public void handle(ActionEvent event) {
                System.out.println("Hello World!");
            }
        });
        
        StackPane root = new StackPane();
        root.getChildren().add(btn);
        primaryStage.setScene(new Scene(root, 300, 250));
        primaryStage.show();
    }
}

Trong ví dụ này, phương thức gọi btn.setOnAction xảy ra khi bạn chọn nút Say 'Hello World'. Phương thức này yêu cầu đối tượng có kiểu EventHandler<ActionEvent>. Interface EventHandler<ActionEvent> bao gồm chỉ một phương thức, là: thay thế thực thi phương thước này bằng một lớp mới, trong ví dụ sử dụng một biểu thức lớp vô danh. Chú ý rằng biểu thức này là đối số truyền vào cho phương thức btn.setOnAction.

Bởi vì interface EventHandler<ActionEvent> chỉ bao gồm một phương thức, bạn có thể sử dụng một biểu thức lambda thay thế biểu thức lớp vô danh. Bạn tìm hiểu nhiều hơn ở phần biểu thức Lambda.

Các lớp vô danh là một ý tưởng cho thực hiện một interface mà chứa từ hai phương thức trở lên. Trong phần highlight mã nguồn tạo một thuộc tính văn bẳn chỉ cho phép nhập giá trị số. Nó định nghĩa lại thực hiện mặc định cho lớp TextField với một lớp vô danh bằng việc ghi đè replaceText và replaceSelection kế thừa từ lớp TextInputControl class.

import javafx.application.Application;
import javafx.event.ActionEvent;
import javafx.event.EventHandler;
import javafx.geometry.Insets;
import javafx.scene.Group;
import javafx.scene.Scene;
import javafx.scene.control.*;
import javafx.scene.layout.GridPane;
import javafx.scene.layout.HBox;
import javafx.stage.Stage;
 
public class CustomTextFieldSample extends Application {
    
    final static Label label = new Label();
 
    @Override
    public void start(Stage stage) {
        Group root = new Group();
        Scene scene = new Scene(root, 300, 150);
        stage.setScene(scene);
        stage.setTitle("Text Field Sample");
 
        GridPane grid = new GridPane();
        grid.setPadding(new Insets(10, 10, 10, 10));
        grid.setVgap(5);
        grid.setHgap(5);
 
        scene.setRoot(grid);
        final Label dollar = new Label("$");
        GridPane.setConstraints(dollar, 0, 0);
        grid.getChildren().add(dollar);
        
        final TextField sum = new TextField() {
            @Override
            public void replaceText(int start, int end, String text) {
                if (!text.matches("[a-z, A-Z]")) {
                    super.replaceText(start, end, text);                     
                }
                label.setText("Enter a numeric value");
            }
 
            @Override
            public void replaceSelection(String text) {
                if (!text.matches("[a-z, A-Z]")) {
                    super.replaceSelection(text);
                }
            }
        };
 
        sum.setPromptText("Enter the total");
        sum.setPrefColumnCount(10);
        GridPane.setConstraints(sum, 1, 0);
        grid.getChildren().add(sum);
        
        Button submit = new Button("Submit");
        GridPane.setConstraints(submit, 2, 0);
        grid.getChildren().add(submit);
        
        submit.setOnAction(new EventHandler<ActionEvent>() {
            @Override
            public void handle(ActionEvent e) {
                label.setText(null);
            }
        });
        
        GridPane.setConstraints(label, 0, 1);
        GridPane.setColumnSpan(label, 3);
        grid.getChildren().add(label);
        
        scene.setRoot(grid);
        stage.show();
    }
 
    public static void main(String[] args) {
        launch(args);
    }
}
Mới hơn Cũ hơn

Biểu mẫu liên hệ