12/08/2018, 15:26

Nested Classes (P3) Inner Class: Anonymous Classes

Anonymous classes giúp code của bạn trở nên ngắn gọn hơn. Nó cho phép bạn khai báo và khởi tạo 1 class ở cùng 1 thời điểm. Nó giống với local classed ngoại trừ việc nó ko có tên. Sử dụng nó nếu bạn cần sử dụng local class chỉ 1 lần. Declaring Anonymous Classes Trong khi local classes cần khai ...

Anonymous classes giúp code của bạn trở nên ngắn gọn hơn. Nó cho phép bạn khai báo và khởi tạo 1 class ở cùng 1 thời điểm. Nó giống với local classed ngoại trừ việc nó ko có tên. Sử dụng nó nếu bạn cần sử dụng local class chỉ 1 lần.

Declaring Anonymous Classes

Trong khi local classes cần khai báo các class, thì anonymous classes là expressions , điều đó có nghĩa là bạn sẽ định nghĩa class ở bên trong một expression khác. Ví dụ bên dưới, HelloWorldAnonymousClasess, sử dụng anonymous classes ở bên trong câu lệnh khởi tạo của frenchGreeting và spanishGreeting, nhưng lại dùng một local class cho việc khởi tạo 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();
    }            
}

Syntax of Anonymous Classes

Như đã nói ở trên, một anonymous class là một expression. Cú pháp của anonymous class expression giống với việc gọi một constructor, ngoại trừ đó là việc định nghĩa class ở bên trong một block code. Xem xét việc khởi tạo của 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 anonymous class gồm các thành phần sau:

  • The new operator
  • Tên của interface cần implement hoặc class cần extend. Trong ví dụ này, anonymous class implement interface HelloWorld
  • Dấu ngoặc đơn chứa arguments của constructor, giống như một biểu thức khởi tạo instance của một class thông thường. Chú ý: Khi bạn implement một interface, nó ko có constructor, nên bạn sẽ sử dụng một cặp dấu ngoặc rỗng, giống như ví dụ này.
  • Body: là phần thân của quá trình khai báo. Một điều đặc biệt, trong body, việc khai báo các method mới được cho phép nhưng statements thì không. Vì một sự định nghĩa anonymous class là một expression, nó phải là 1 phần của một statement. Trong ví dụ này, nó là một phần của câu lệnh khởi tạo frenchGreeting object (Giải thích cho việc tại sao có một dấu ";" ở phía sau dấu đóng ngoặc).

Accessing Local Variables of the Enclosing Scope, and Declaring and Accessing Members of the Anonymous Class

Giống local classes, anonymous classes có thể capture variables, nó cũng giống với local class về việc truy cập đến local variables của enclosing scope:

  • Một anonymous class có thể truy cập đến các member của enclosing class.
  • Một anonymous class không thể truy cập đến các local variables của enclosing scope nếu chúng không được khai báo là final hoặc effectively final
  • Giống như một nested class, một khai báo trong anonymous class shadows mọi khai báo khác cùng tên ở enclosing scope.
  • Xem thêm về shadowing ở phần 1: https://viblo.asia/ninh.nguyen.ke/posts/XL6lAY2mlek

Anonymous classes cũng có những hạn chế giống local classes với các member của chúng:

  • Bạn không thể khai báo các khởi tạo static hoặc interface trong anonymous classes
  • Một anonymous class có thể có các static member, tuy nhiên chúng phải là constant variables. Xem thêm constant variables ở phần 2: https://viblo.asia/ninh.nguyen.ke/posts/OeVKBw6AZkW

Bạn có thể khai báo những cái dưới đây trong anonymous classes:

  • Fields
  • Extra methods (ngay cả khi nó ko phải là method implement từ super class)
  • Instance initializers
  • Local classes

Tuy nhiên, bạn không thể khai báo constructor bên trong một anonymous class.

Examples of Anonymous Classes

JavaFX:

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, method btn.setAction được gọi đến khi bạn select vào "Hello World" button. Method này yêu cầu một object có type là EventHandler<ActionEvent>. EventHandler<ActionEvent> interface chỉ chứa 1 method, đó là "handle". Thay vì phải implement method bằng cách tạo ra một class mới, trong ví dụ này đã sử dụng anonymouse class expression. Chú ý rằng anonymouse class expression này chính là một đối số được truyền vào bên trong method btn.setOnAction. Vì EventHandler<ActionEvent> interface chỉ chứa đúng 1 method, bạn có thể sử dụng lambda expression thay cho anonymous class expression. Ở phần tiếp theo sẽ giới thiệu về Lambda Expression.

Nguồn bài viết

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

0