匿名类

原文: https://docs.oracle.com/javase/tutorial/java/javaOO/anonymousclasses.html

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

本节包括以下主题:

虽然本地类是类声明,但匿名类是表达式,这意味着您在另一个表达式中定义该类。以下示例 HelloWorldAnonymousClasses 在局部变量frenchGreetingspanishGreeting的初始化语句中使用匿名类,但使用本地类初始化变量englishGreeting :

  1. public class HelloWorldAnonymousClasses {
  2. interface HelloWorld {
  3. public void greet();
  4. public void greetSomeone(String someone);
  5. }
  6. public void sayHello() {
  7. class EnglishGreeting implements HelloWorld {
  8. String name = "world";
  9. public void greet() {
  10. greetSomeone("world");
  11. }
  12. public void greetSomeone(String someone) {
  13. name = someone;
  14. System.out.println("Hello " + name);
  15. }
  16. }
  17. HelloWorld englishGreeting = new EnglishGreeting();
  18. HelloWorld frenchGreeting = new HelloWorld() {
  19. String name = "tout le monde";
  20. public void greet() {
  21. greetSomeone("tout le monde");
  22. }
  23. public void greetSomeone(String someone) {
  24. name = someone;
  25. System.out.println("Salut " + name);
  26. }
  27. };
  28. HelloWorld spanishGreeting = new HelloWorld() {
  29. String name = "mundo";
  30. public void greet() {
  31. greetSomeone("mundo");
  32. }
  33. public void greetSomeone(String someone) {
  34. name = someone;
  35. System.out.println("Hola, " + name);
  36. }
  37. };
  38. englishGreeting.greet();
  39. frenchGreeting.greetSomeone("Fred");
  40. spanishGreeting.greet();
  41. }
  42. public static void main(String... args) {
  43. HelloWorldAnonymousClasses myApp =
  44. new HelloWorldAnonymousClasses();
  45. myApp.sayHello();
  46. }
  47. }

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

考虑frenchGreeting对象的实例化:

  1. HelloWorld frenchGreeting = new HelloWorld() {
  2. String name = "tout le monde";
  3. public void greet() {
  4. greetSomeone("tout le monde");
  5. }
  6. public void greetSomeone(String someone) {
  7. name = someone;
  8. System.out.println("Salut " + name);
  9. }
  10. };

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

  • new运算符

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

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

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

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

像本地类一样,匿名类可以捕获变量;它们对封闭范围的局部变量具有相同的访问权限:

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

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

  • 与嵌套类一样,匿名类中的类型(例如变量)的声明会影响封闭范围中具有相同名称的任何其他声明。有关详细信息,请参阅 Shadowing

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

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

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

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

  • 字段

  • 额外的方法(即使他们没有实现超类型的任何方法)

  • 实例初始化器

  • 本地类

但是,您不能在匿名类中声明构造器。

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

考虑 JavaFX 示例 HelloWorld.java (来自 Hello World,来自 JavaFX 入门的 JavaFX Style )。此示例创建一个包含 Say’Hello World’按钮的框架。匿名类表达式突出显示:

  1. import javafx.event.ActionEvent;
  2. import javafx.event.EventHandler;
  3. import javafx.scene.Scene;
  4. import javafx.scene.control.Button;
  5. import javafx.scene.layout.StackPane;
  6. import javafx.stage.Stage;
  7. public class HelloWorld extends Application {
  8. public static void main(String[] args) {
  9. launch(args);
  10. }
  11. @Override
  12. public void start(Stage primaryStage) {
  13. primaryStage.setTitle("Hello World!");
  14. Button btn = new Button();
  15. btn.setText("Say 'Hello World'");
  16. btn.setOnAction(new EventHandler<ActionEvent>() {
  17. @Override
  18. public void handle(ActionEvent event) {
  19. System.out.println("Hello World!");
  20. }
  21. });
  22. StackPane root = new StackPane();
  23. root.getChildren().add(btn);
  24. primaryStage.setScene(new Scene(root, 300, 250));
  25. primaryStage.show();
  26. }
  27. }

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

因为EventHandler&lt;ActionEvent>接口只包含一个方法,所以可以使用 lambda 表达式而不是匿名类表达式。有关更多信息,请参见 Lambda 表达式部分。

匿名类是实现包含两个或更多方法的接口的理想选择。以下 JavaFX 示例来自 UI 控件自定义部分。突出显示的代码创建一个仅接受数值的文本字段。它通过覆盖从TextInputControl类继承的replaceTextreplaceSelection方法,重新定义了具有匿名类的TextField类的默认实现。

  1. import javafx.application.Application;
  2. import javafx.event.ActionEvent;
  3. import javafx.event.EventHandler;
  4. import javafx.geometry.Insets;
  5. import javafx.scene.Group;
  6. import javafx.scene.Scene;
  7. import javafx.scene.control.*;
  8. import javafx.scene.layout.GridPane;
  9. import javafx.scene.layout.HBox;
  10. import javafx.stage.Stage;
  11. public class CustomTextFieldSample extends Application {
  12. final static Label label = new Label();
  13. @Override
  14. public void start(Stage stage) {
  15. Group root = new Group();
  16. Scene scene = new Scene(root, 300, 150);
  17. stage.setScene(scene);
  18. stage.setTitle("Text Field Sample");
  19. GridPane grid = new GridPane();
  20. grid.setPadding(new Insets(10, 10, 10, 10));
  21. grid.setVgap(5);
  22. grid.setHgap(5);
  23. scene.setRoot(grid);
  24. final Label dollar = new Label("$");
  25. GridPane.setConstraints(dollar, 0, 0);
  26. grid.getChildren().add(dollar);
  27. final TextField sum = new TextField() {
  28. @Override
  29. public void replaceText(int start, int end, String text) {
  30. if (!text.matches("[a-z, A-Z]")) {
  31. super.replaceText(start, end, text);
  32. }
  33. label.setText("Enter a numeric value");
  34. }
  35. @Override
  36. public void replaceSelection(String text) {
  37. if (!text.matches("[a-z, A-Z]")) {
  38. super.replaceSelection(text);
  39. }
  40. }
  41. };
  42. sum.setPromptText("Enter the total");
  43. sum.setPrefColumnCount(10);
  44. GridPane.setConstraints(sum, 1, 0);
  45. grid.getChildren().add(sum);
  46. Button submit = new Button("Submit");
  47. GridPane.setConstraints(submit, 2, 0);
  48. grid.getChildren().add(submit);
  49. submit.setOnAction(new EventHandler<ActionEvent>() {
  50. @Override
  51. public void handle(ActionEvent e) {
  52. label.setText(null);
  53. }
  54. });
  55. GridPane.setConstraints(label, 0, 1);
  56. GridPane.setColumnSpan(label, 3);
  57. grid.getChildren().add(label);
  58. scene.setRoot(grid);
  59. stage.show();
  60. }
  61. public static void main(String[] args) {
  62. launch(args);
  63. }
  64. }