# 匿名类

> 原文： [https://docs.oracle.com/javase/tutorial/java/javaOO/anonymousclasses.html](https://docs.oracle.com/javase/tutorial/java/javaOO/anonymousclasses.html)

匿名类使您可以使代码更简洁。它们使您能够同时声明和实例化一个类。他们就像当地的类，除了他们没有名字。如果您只需要使用本地类一次，请使用它们。

本节包括以下主题：

*   [声明匿名类](#declaring-anonymous-classes)
*   [匿名类的语法](#syntax-of-anonymous-classes)
*   [访问封闭范围的局部变量，以及声明和访问匿名类的成员](#accessing)
*   [匿名类的例子](#examples-of-anonymous-classes)

虽然本地类是类声明，但匿名类是表达式，这意味着您在另一个表达式中定义该类。以下示例 [`HelloWorldAnonymousClasses`](examples/HelloWorldAnonymousClasses.java) 在局部变量`frenchGreeting`和`spanishGreeting`的初始化语句中使用匿名类，但使用本地类初始化变量`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();
    }            
}

```

如前所述，匿名类是表达式。匿名类表达式的语法类似于构造函数的调用，除了代码块中包含类定义。

考虑`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);
            }
        };
```

匿名类表达式包含以下内容：

*   `new`运算符

*   要实现的接口的名称或要扩展的类。在此示例中，匿名类正在实现接口`HelloWorld`。

*   括号，包含构造函数的参数，就像普通的类实例创建表达式一样。 **注意**：当你实现一个接口时，没有构造函数，所以你使用一对空括号，如本例所示。

*   身体，是一个类声明体。更具体地说，在正文中，方法声明是允许的，但语句不是。

因为匿名类定义是表达式，所以它必须是语句的一部分。在此示例中，匿名类表达式是实例化`frenchGreeting`对象的语句的一部分。 （这解释了为什么在结束括号后有分号。）

像本地类一样，匿名类可以[捕获变量](localclasses.html#accessing-members-of-an-enclosing-class);它们对封闭范围的局部变量具有相同的访问权限：

*   匿名类可以访问其封闭类的成员。

*   匿名类无法访问其封闭范围中未声明为`final`或实际为 final 的局部变量。

*   与嵌套类一样，匿名类中的类型（例如变量）的声明会影响封闭范围中具有相同名称的任何其他声明。有关详细信息，请参阅 [Shadowing](../../java/javaOO/nested.html#shadowing) 。

匿名类与其成员的本地类也有相同的限制：

*   您不能在匿名类中声明静态初始化程序或成员接口。

*   匿名类可以具有静态成员，前提是它们是常量变量。

请注意，您可以在匿名类中声明以下内容：

*   字段

*   额外的方法（即使他们没有实现超类型的任何方法）

*   实例初始化器

*   本地类

但是，您不能在匿名类中声明构造函数。

匿名类通常用于图形用户界面（GUI）应用程序。

考虑 JavaFX 示例 [`HelloWorld.java`](https://docs.oracle.com/javase/8/javafx/get-started-tutorial/hello_world.htm) （来自 [Hello World，来自](https://docs.oracle.com/javase/8/javafx/get-started-tutorial/hello_world.htm) [JavaFX 入门](https://docs.oracle.com/javase/8/javafx/get-started-tutorial/javafx_get_started.htm)的 JavaFX Style ）。此示例创建一个包含 **Say'Hello World'**按钮的框架。匿名类表达式突出显示：

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

在此示例中，方法调用`btn.setOnAction`指定当您选择 **Say'Hello World'**按钮时会发生什么。此方法需要`EventHandler&lt;ActionEvent&gt;`类型的对象。 `EventHandler&lt;ActionEvent&gt;`接口只包含一个方法 handle。该示例使用匿名类表达式，而不是使用新类实现此方法。请注意，此表达式是传递给`btn.setOnAction`方法的参数。

因为`EventHandler&lt;ActionEvent&gt;`接口只包含一个方法，所以可以使用 lambda 表达式而不是匿名类表达式。有关更多信息，请参见 [Lambda 表达式](lambdaexpressions.html)部分。

匿名类是实现包含两个或更多方法的接口的理想选择。以下 JavaFX 示例来自 [UI 控件自定义](https://docs.oracle.com/javase/8/javafx/user-interface-tutorial/custom.htm)部分。突出显示的代码创建一个仅接受数值的文本字段。它通过覆盖从`TextInputControl`类继承的`replaceText`和`replaceSelection`方法，重新定义了具有匿名类的`TextField`类的默认实现。

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